diff --git a/modules/processes/mill/raster/2.5D b/modules/processes/mill/raster/2.5D index ed9d0faca262406da0e0c675232ac58184acd5ec..319a30a444e29f34e4d0f2ff66328d1a28b25236 100644 --- a/modules/processes/mill/raster/2.5D +++ b/modules/processes/mill/raster/2.5D @@ -56,8 +56,8 @@ var inputs = { // // calculation in progress, draw and accumulate // - draw_layer(evt.detail) - accumulate_layer(evt.detail) + draw_path(evt.detail) + accumulate_path(evt.detail) mod.offsetCount += 1 if ((mod.offsetCount != parseInt(mod.number.value)) && (evt.detail.length > 0)) { @@ -73,7 +73,7 @@ var inputs = { // layer loop not complete // merge_layer() - accumulate_path() + accumulate_toolpath() clear_layer() mod.depthmm += parseFloat(mod.cut_mm.value) if (mod.depthmm > parseFloat(mod.max_mm.value)) { @@ -98,8 +98,8 @@ var inputs = { // // done, finish and output // - //draw_path(mod.path) - //draw_connections() + draw_path(mod.toolpath) + draw_connections(mod.toolpath) mod.label.nodeValue = 'calculate' mod.labelspan.style.fontWeight = 'normal' outputs.toolpath.event() @@ -422,11 +422,11 @@ function clear_layer() { svg.appendChild(g) } // -// accumulate_layer +// accumulate_path // todo: replace inefficient insertion sort // todo: move sort out of main thread // -function accumulate_layer(path) { +function accumulate_path(path) { var forward = mod.forward.checked var conventional = mod.conventional.checked var sort = mod.sort.checked @@ -485,9 +485,9 @@ function merge_layer() { } } // -// accumulate_path +// accumulate_toolpath // -function accumulate_path() { +function accumulate_toolpath() { for (var seg = 0; seg < mod.path.length; ++seg) { var newseg = [] for (var pt = 0; pt < mod.path[seg].length; ++pt) { @@ -499,55 +499,9 @@ function accumulate_path() { } } // -// add_depth -// -function add_depth() { - var cut = parseFloat(mod.cut_in.value) - var max = parseFloat(mod.max_in.value) - var newpath = [] - for (var seg = 0; seg < mod.path.length; ++seg) { - var depth = cut - if (mod.path[seg][0][0] == mod.path[seg][mod.path[seg].length-1][0]) { - var newseg = [] - while (depth <= max) { - var idepth = -Math.round(mod.dpi*depth) - for (var pt = 0; pt < mod.path[seg].length; ++pt) { - var point = mod.path[seg][pt].concat(idepth) - newseg.splice(newseg.length,0,point) - } - if (depth == max) - break - depth += cut - if (depth > max) - depth = max - } - newpath.splice(newpath.length,0,newseg) - } - else { - var newseg = [] - while (depth <= max) { - var idepth = -Math.round(mod.dpi*depth) - for (var pt = 0; pt < mod.path[seg].length; ++pt) { - var point = mod.path[seg][pt].concat(idepth) - newseg.splice(newseg.length,0,point) - } - newpath.splice(newpath.length,0,newseg) - newseg = [] - if (depth == max) - break - depth += cut - if (depth > max) - depth = max - } - } - } - mod.path = newpath - mod.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi) - } -// -// draw_layer +// draw_path // -function draw_layer(path) { +function draw_path(path) { var g = document.getElementById(mod.div.id+'g') var h = mod.img.height var w = mod.img.width @@ -601,14 +555,14 @@ function draw_layer(path) { // // draw_connections // -function draw_connections() { +function draw_connections(path) { var g = document.getElementById(mod.div.id+'g') var h = mod.img.height var w = mod.img.width // // loop over segments // - for (var segment = 1; segment < mod.path.length; ++segment) { + for (var segment = 1; segment < path.length; ++segment) { // // draw connection from previous segment // @@ -617,10 +571,10 @@ function draw_connections() { line.setAttribute('stroke','red') line.setAttribute('stroke-width',1) line.setAttribute('stroke-linecap','round') - var x1 = mod.path[segment-1][mod.path[segment-1].length-1][0] - var y1 = h-mod.path[segment-1][mod.path[segment-1].length-1][1]-1 - var x2 = mod.path[segment][0][0] - var y2 = h-mod.path[segment][0][1]-1 + var x1 = path[segment-1][path[segment-1].length-1][0] + var y1 = h-path[segment-1][path[segment-1].length-1][1]-1 + var x2 = path[segment][0][0] + var y2 = h-path[segment][0][1]-1 line.setAttribute('x1',x1) line.setAttribute('y1',y1) line.setAttribute('x2',x2) diff --git a/programs/processes/mill/raster/2.5D b/programs/processes/mill/raster/2.5D index c28713efba32e111e22e9e816411e05294605bff..8bd1d9d07277cb1f729da15f9077b7e77d180dd2 100644 --- a/programs/processes/mill/raster/2.5D +++ b/programs/processes/mill/raster/2.5D @@ -1 +1 @@ -{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"451.73416597015654","left":"3167.040204340621","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"801.7341659701567","left":"3651.040204340621","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"895.7341659701567","left":"3205.040204340621","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"352.73416597015677","left":"2751.040204340621","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"800.7341659701567","left":"2786.040204340621","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1323.2919985029002","left":"1653.9305600175123","inputs":{},"outputs":{}},"0.7562574507163453":{"definition":"//\n// ShopBot\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n\n\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'ShopBot'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = '20'\n mod.plungespeed.value = '20'\n mod.jogspeed.value = '75'\n mod.jogheight.value = '5'\n mod.spindlespeed.value = '10000'\n mod.unitsin.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.path = evt.detail.path\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n make_path()\n }}}\n//\n// outputs\n//\nvar outputs = {\n file:{type:'object',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".sbp\"\n obj.contents = str\n mods.output(mod,'file',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // cut speed\n //\n div.appendChild(document.createTextNode('cut speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.cutspeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // plunge speed\n //\n div.appendChild(document.createTextNode('plunge speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.plungespeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // jog speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // jog height\n //\n div.appendChild(document.createTextNode('jog height: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogheight = input\n div.appendChild(document.createTextNode(' (mm)'))\n div.appendChild(document.createElement('br'))\n //\n // spindle speed\n //\n div.appendChild(document.createTextNode('spindle speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.spindlespeed = input\n div.appendChild(document.createTextNode(' (RPM)'))\n div.appendChild(document.createElement('br'))\n //\n // file units\n //\n div.appendChild(document.createTextNode('file units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsin'\n div.appendChild(input)\n mod.unitsin = input\n div.appendChild(document.createTextNode('in'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsmm'\n div.appendChild(input)\n mod.unitsmm = input\n div.appendChild(document.createTextNode('mm'))\n }\n//\n// local functions\n//\nfunction make_path() {\n if (mod.unitsin.checked)\n var units = 1\n else\n var units = 25.4\n var dx = units*mod.width/mod.dpi\n var nx = mod.width\n var cut_speed = units*parseFloat(mod.cutspeed.value)/25.4\n var plunge_speed = units*parseFloat(mod.plungespeed.value)/25.4\n var jog_speed = units*parseFloat(mod.jogspeed.value)/25.4\n var jog_height = units*parseFloat(mod.jogheight.value)/25.4\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var scale = dx/(nx-1)\n str = \"SA\\r\\n\" // set to absolute distances\n str += \"TR,\"+spindle_speed+\",1\\r\\n\" // set spindle speed\n str += \"SO,1,1\\r\\n\" // set output number 1 to on\n str += \"pause 2\\r\\n\" // let spindle come up to speed\n str += \"MS,\"+cut_speed.toFixed(4)+\",\"+plunge_speed.toFixed(4)+\"\\r\\n\" // set xy,z speed\n str += \"JS,\"+jog_speed.toFixed(4)+\",\"+jog_speed.toFixed(4)+\"\\r\\n\" // set jog xy,z speed\n str += \"JZ,\"+jog_height.toFixed(4)+\"\\r\\n\" // move up\n //\n // follow segments\n //\n for (var seg = 0; seg < mod.path.length; ++seg) {\n //\n // move up to starting point\n //\n x = scale*mod.path[seg][0][0]\n y = scale*mod.path[seg][0][1]\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n str += \"J2,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\"\\r\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"MZ,\"+z.toFixed(4)+\"\\r\\n\"\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\n //\n // move to next point\n //\n x = scale*mod.path[seg][pt][0]\n y = scale*mod.path[seg][pt][1]\n z = scale*mod.path[seg][pt][2]\n str += \"M3,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\",\"+z.toFixed(4)+\"\\r\\n\"\n }\n }\n //\n // output file\n //\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n outputs.file.event(str)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"955.6926005771381","left":"1652.1709212620551","inputs":{},"outputs":{}},"0.3040697193095865":{"definition":"//\n// mesh rotate\n// \n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mesh rotate'\n//\n// initialization\n//\nvar init = function() {\n mod.rx.value = '0'\n mod.ry.value = '0'\n mod.rz.value = '0'\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = evt.detail\n rotate_mesh()}}}\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // rotation\n //\n div.appendChild(document.createTextNode('rotation (degrees):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rx = input\n div.appendChild(document.createTextNode(' y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.ry = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' z: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rz = input\n div.appendChild(document.createTextNode(' (enter)'))\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var text = document.createTextNode('dx:')\n div.appendChild(text)\n mod.dxn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy:')\n div.appendChild(text)\n mod.dyn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz:')\n div.appendChild(text)\n mod.dzn = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// rotate mesh\n//\nfunction rotate_mesh() {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(mod.mesh)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits, rotate, and draw\n //\n var blob = new Blob(['('+rotate_mesh_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // mesh\n //\n mod.mesh = evt.data.mesh\n //\n // output\n //\n outputs.mesh.event(evt.data.rotate)\n })\n //\n // call worker\n //\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n rx:rx,ry:ry,rz:rz,\n image:img.data.buffer,mesh:mod.mesh},\n [img.data.buffer,mod.mesh])\n }\nfunction rotate_mesh_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n //return([x3,y3,z3])\n return({x:x3,y:y3,z:z3})\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var rx = evt.data.rx\n var ry = evt.data.ry\n var rz = evt.data.rz\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0.x > xmax) xmax = p0.x\n if (p0.x < xmin) xmin = p0.x\n if (p0.y > ymax) ymax = p0.y\n if (p0.y < ymin) ymin = p0.y\n if (p0.z > zmax) zmax = p0.z\n if (p0.z < zmin) zmin = p0.z\n var p1 = rotate(x1,y1,z1)\n if (p1.x > xmax) xmax = p1.x\n if (p1.x < xmin) xmin = p1.x\n if (p1.y > ymax) ymax = p1.y\n if (p1.y < ymin) ymin = p1.y\n if (p1.z > zmax) zmax = p1.z\n if (p1.z < zmin) zmin = p1.z\n var p2 = rotate(x2,y2,z2)\n if (p2.x > xmax) xmax = p2.x\n if (p2.x < xmin) xmin = p2.x\n if (p2.y > ymax) ymax = p2.y\n if (p2.y < ymin) ymin = p2.y\n if (p2.z > zmax) zmax = p2.z\n if (p2.z < zmin) zmin = p2.z\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // copy mesh\n //\n var newbuf = evt.data.mesh.slice(0)\n var newview = new DataView(newbuf)\n //\n // copy and draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = (width-1)\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = (height-1)\n }\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n line(p0.x,p0.y,p1.x,p1.y)\n line(p1.x,p1.y,p2.x,p2.y)\n line(p2.x,p2.y,p0.x,p0.y)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.z,endian)\n newoffset += 4\n newoffset += 2\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh,rotate:newbuf},\n [evt.data.image,evt.data.mesh,newbuf])\n self.close()\n })\n }\nfunction old_rotate_mesh() {\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n return([x3,y3,z3])\n }\n //\n // get vars\n //\n var view = mod.mesh\n var endian = true\n var triangles = view.getUint32(80,endian)\n mod.triangles = triangles\n var size = 80+4+triangles*(4*12+2)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0[0] > xmax) xmax = p0[0]\n if (p0[0] < xmin) xmin = p0[0]\n if (p0[1] > ymax) ymax = p0[1]\n if (p0[1] < ymin) ymin = p0[1]\n if (p0[2] > zmax) zmax = p0[2]\n if (p0[2] < zmin) zmin = p0[2]\n var p1 = rotate(x1,y1,z1)\n if (p1[0] > xmax) xmax = p1[0]\n if (p1[0] < xmin) xmin = p1[0]\n if (p1[1] > ymax) ymax = p1[1]\n if (p1[1] < ymin) ymin = p1[1]\n if (p1[2] > zmax) zmax = p1[2]\n if (p1[2] < zmin) zmin = p1[2]\n var p2 = rotate(x2,y2,z2)\n if (p2[0] > xmax) xmax = p2[0]\n if (p2[0] < xmin) xmin = p2[0]\n if (p2[1] > ymax) ymax = p2[1]\n if (p2[1] < ymin) ymin = p2[1]\n if (p2[2] > zmax) zmax = p2[2]\n if (p2[2] < zmin) zmin = p2[2]\n }\n mod.dx = xmax-xmin\n mod.dy = ymax-ymin\n mod.dz = zmax-zmin\n mod.dxn.nodeValue = 'dx: '+mod.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+mod.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+mod.dz.toFixed(3)\n mod.xmin = xmin\n mod.ymin = ymin\n mod.zmin = zmin\n mod.xmax = xmax\n mod.ymax = ymax\n mod.zmax = zmax\n //\n // copy mesh\n //\n var buf = mod.mesh.buffer.slice(0)\n var newview = new DataView(buf)\n //\n // draw projection and save rotation\n //\n var ctx = mod.meshcanvas.getContext('2d')\n var w = mod.meshcanvas.width\n var h = mod.meshcanvas.height\n ctx.clearRect(0,0,w,h)\n var dx = mod.dx\n var dy = mod.dy\n if (dx > dy) {\n var xo = 0\n var yo = h*.5*(1-dy/dx)\n var xw = w\n var yh = w*dy/dx\n }\n else {\n var xo = w*.5*(1-dx/dy)\n var yo = 0\n var xw = h*dx/dy\n var yh = h\n }\n ctx.beginPath()\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n x0 = xo+xw*(p0[0]-xmin)/dx\n y0 = yo+yh*(ymax-p0[1])/dy\n x1 = xo+xw*(p1[0]-xmin)/dx\n y1 = yo+yh*(ymax-p1[1])/dy\n x2 = xo+xw*(p2[0]-xmin)/dx\n y2 = yo+yh*(ymax-p2[1])/dy\n ctx.moveTo(x0,y0)\n ctx.lineTo(x1,y1)\n ctx.lineTo(x2,y2)\n ctx.lineTo(x0,y0)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[2],endian)\n newoffset += 4\n newoffset += 2\n }\n ctx.stroke()\n //\n // generate output\n //\n outputs.mesh.event(buf)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"454.4270833093813","left":"1179.6997327249337","inputs":{},"outputs":{}},"0.8910984899438215":{"definition":"//\n// read stl\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read STL'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n }\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n stl_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select stl file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('name: ')\n info.appendChild(text)\n mod.namen = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('size: ')\n info.appendChild(text)\n mod.sizen = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('triangles: ')\n info.appendChild(text)\n mod.trianglesn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dx: ')\n info.appendChild(text)\n mod.dxn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy: ')\n info.appendChild(text)\n mod.dyn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz: ')\n info.appendChild(text)\n mod.dzn = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction stl_read_handler(event) {\n var file_reader = new FileReader()\n file_reader.onload = stl_load_handler\n input_file = mod.file.files[0]\n file_name = input_file.name\n mod.namen.nodeValue = 'name: '+file_name\n file_reader.readAsArrayBuffer(input_file)\n }\n//\n// load handler\n//\nfunction stl_load_handler(event) {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(event.target.result)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n if (size != view.byteLength) {\n mod.sizen.nodeValue = 'error: not binary STL'\n mod.trianglesn.nodeValue = ''\n mod.dxn.nodeValue = ''\n mod.dyn.nodeValue = ''\n mod.dzn.nodeValue = ''\n return\n }\n mod.sizen.nodeValue = 'size: '+size\n mod.trianglesn.nodeValue = 'triangles: '+triangles\n //\n // find limits and draw\n //\n var blob = new Blob(['('+draw_limits_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // output\n //\n outputs.mesh.event(evt.data.mesh)\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n image:img.data.buffer,mesh:event.target.result},\n [img.data.buffer,event.target.result])\n }\nfunction draw_limits_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n y0 = view.getFloat32(offset,endian)\n offset += 4\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n z0 = view.getFloat32(offset,endian)\n offset += 4\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n x1 = view.getFloat32(offset,endian)\n offset += 4\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n y1 = view.getFloat32(offset,endian)\n offset += 4\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n z1 = view.getFloat32(offset,endian)\n offset += 4\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n x2 = view.getFloat32(offset,endian)\n offset += 4\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n y2 = view.getFloat32(offset,endian)\n offset += 4\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n z2 = view.getFloat32(offset,endian)\n offset += 4\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n offset += 2\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = width-1\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = height-1\n }\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n line(x0,y0,x1,y1)\n line(x1,y1,x2,y2)\n line(x2,y2,x0,y0)\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"292.47576938638093","left":"773.912070549265","inputs":{},"outputs":{}},"0.20905178335446428":{"definition":"//\n// mesh slice raster\n// \n// todo\n// include slice plane triangles\n// scale perturbation to resolution\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mesh slice raster'\n//\n// initialization\n//\nvar init = function() {\n mod.mmunits.value = '25.4'\n mod.inunits.value = '1'\n mod.depth.value = '2.9999999999999996'\n mod.width.value = '1000'\n mod.border.value = '0'\n mod.delta = 1e-6\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = new DataView(evt.detail)\n find_limits_slice()}},\n settings:{type:'',\n event:function(evt){\n for (var p in evt.detail)\n if (p == 'depthmm') {\n mod.depth.value = evt.detail[p]\n /parseFloat(mod.mmunits.value)\n }\n find_limits_slice()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)\n }},\n imageInfo:{type:'',\n event:function(){\n var obj = {}\n obj.name = \"mesh slice raster\"\n obj.width = mod.img.width\n obj.height = mod.img.height\n obj.dpi = mod.img.width/(mod.dx*parseFloat(mod.inunits.value))\n mods.output(mod,'imageInfo',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen slice canvas\n //\n div.appendChild(document.createTextNode(' '))\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.slicecanvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // mesh units\n //\n div.appendChild(document.createTextNode('mesh units: (enter)'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.inunits.value = parseFloat(mod.mmunits.value)/25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.mmunits = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.mmunits.value = parseFloat(mod.inunits.value)*25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.inunits = input\n //\n // mesh size\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mesh size:'))\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (units)')\n div.appendChild(text)\n mod.meshsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (mm)')\n div.appendChild(text)\n mod.mmsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (in)')\n div.appendChild(text)\n mod.insize = text\n //\n // slice depth\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice Z depth: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.depth = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice border \n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice border: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.border = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice width\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice width: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.width = input\n div.appendChild(document.createTextNode(' (pixels)'))\n //\n // view slice\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view slice'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// find limits then slice\n//\nfunction find_limits_slice() {\n var blob = new Blob(['('+limits_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n mod.triangles = evt.data.triangles\n mod.xmin = evt.data.xmin\n mod.xmax = evt.data.xmax\n mod.ymin = evt.data.ymin\n mod.ymax = evt.data.ymax\n mod.zmin = evt.data.zmin\n mod.zmax = evt.data.zmax\n mod.dx = mod.xmax-mod.xmin\n mod.dy = mod.ymax-mod.ymin\n mod.dz = mod.zmax-mod.zmin\n mod.meshsize.nodeValue = \n mod.dx.toFixed(3)+' x '+\n mod.dy.toFixed(3)+' x '+\n mod.dz.toFixed(3)+' (units)'\n var mm = parseFloat(mod.mmunits.value)\n mod.mmsize.nodeValue = \n (mod.dx*mm).toFixed(3)+' x '+\n (mod.dy*mm).toFixed(3)+' x '+\n (mod.dz*mm).toFixed(3)+' (mm)'\n var inches = parseFloat(mod.inunits.value)\n mod.insize.nodeValue = \n (mod.dx*inches).toFixed(3)+' x '+\n (mod.dy*inches).toFixed(3)+' x '+\n (mod.dz*inches).toFixed(3)+' (in)'\n mods.fit(mod.div)\n slice_mesh()\n })\n var border = parseFloat(mod.border.value)\n webworker.postMessage({\n mesh:mod.mesh,\n border:border,delta:mod.delta})\n }\nfunction limits_worker() {\n self.addEventListener('message',function(evt) {\n var view = evt.data.mesh\n var depth = evt.data.depth\n var border = evt.data.border\n var delta = evt.data.delta // perturb to remove degeneracies\n //\n // get vars\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n offset += 2\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n }\n xmin -= border\n xmax += border\n ymin -= border\n ymax += border\n //\n // return\n //\n self.postMessage({triangles:triangles,\n xmin:xmin,xmax:xmax,ymin:ymin,ymax:ymax,\n zmin:zmin,zmax:zmax})\n self.close()\n })\n }\n//\n// slice mesh\n// \nfunction slice_mesh() {\n var blob = new Blob(['('+slice_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.slicecanvas.height*.5*(1-h/w)\n var wd = mod.slicecanvas.width\n var hd = mod.slicecanvas.width*h/w\n }\n else {\n var x0 = mod.slicecanvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.slicecanvas.height*w/h\n var hd = mod.slicecanvas.height\n }\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n outputs.image.event()\n outputs.imageInfo.event()\n })\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n var depth = parseFloat(mod.depth.value)\n mod.img.width = parseInt(mod.width.value)\n mod.img.height = Math.round(mod.img.width*mod.dy/mod.dx)\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.img.height,width:mod.img.width,depth:depth,\n imgbuffer:img.data.buffer,mesh:mod.mesh,\n xmin:mod.xmin,xmax:mod.xmax,\n ymin:mod.ymin,ymax:mod.ymax,\n zmin:mod.zmin,zmax:mod.zmax,\n delta:mod.delta},\n [img.data.buffer])\n }\nfunction slice_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var depth = evt.data.depth\n var view = evt.data.mesh\n var delta = evt.data.delta // perturb to remove degeneracies\n var xmin = evt.data.xmin\n var xmax = evt.data.xmax\n var ymin = evt.data.ymin\n var ymax = evt.data.ymax\n var zmin = evt.data.zmin\n var zmax = evt.data.zmax\n var buf = new Uint8ClampedArray(evt.data.imgbuffer)\n //\n // get vars from buffer\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // initialize slice image\n //\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // find triangles crossing the slice\n //\n var segs = []\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n //\n // assemble vertices\n //\n offset += 2\n var v = [[x0,y0,z0],[x1,y1,z1],[x2,y2,z2]]\n //\n // sort z\n //\n v.sort(function(a,b) {\n if (a[2] < b[2])\n return -1\n else if (a[2] > b[2])\n return 1\n else\n return 0\n })\n //\n // check for crossings\n //\n if ((v[0][2] < (zmax-depth)) && (v[2][2] > (zmax-depth))) {\n //\n // crossing found, check for side and save\n //\n if (v[1][2] < (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[2][0]+(v[1][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n var y1 = v[2][1]+(v[1][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n }\n else if (v[1][2] >= (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[1][0]+(v[0][0]-v[1][0])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n var y1 = v[1][1]+(v[0][1]-v[1][1])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n }\n if (y0 < y1)\n segs.push({x0:x0,y0:y0,x1:x1,y1:y1})\n else\n segs.push({x0:x1,y0:y1,x1:x0,y1:y0})\n }\n }\n //\n // fill interior\n //\n for (var row = 0; row < h; ++row) {\n var y = ymin+(ymax-ymin)*row/(h-1)\n rowsegs = segs.filter(p => ((p.y0 <= y) && (p.y1 >= y)))\n var xs = rowsegs.map(p =>\n (p.x0+(p.x1-p.x0)*(y-p.y0)/(p.y1-p.y0)))\n xs.sort((a,b) => (a-b))\n for (var col = 0; col < w; ++col) {\n var x = xmin+(xmax-xmin)*col/(w-1)\n var index = xs.findIndex((p) => (p >= x))\n if (index == -1)\n var i = 0\n else\n var i = 255*(index%2)\n buf[(h-1-row)*w*4+col*4+0] = i\n buf[(h-1-row)*w*4+col*4+1] = i\n buf[(h-1-row)*w*4+col*4+2] = i\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // output the slice\n //\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"300.6601951095721","left":"1614.31672208591","inputs":{},"outputs":{}},"0.5791854769792683":{"definition":"//\n// offset\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = '25'\n mod.distances = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n if ((mod.offset.value != '') && (mod.distances != ''))\n offset()\n else\n mod.distances = ''\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"311.1716402604384","left":"3724.071002771963","inputs":{},"outputs":{}},"0.5086051394329043":{"definition":"//\n// three.js library\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'three.js library'\n//\n// initialization\n//\nvar init = function() {\n mod.library = {}\n store_library()\n list_library()\n }\n//\n// inputs\n//\nvar inputs = {\n store:{type:'object',\n event:function(evt) {\n store_object(evt.detail)\n }},\n request:{type:'key',\n event:function(evt) {\n outputs.response.event(mod.library[evt.detail])\n }}}\n//\n// outputs\n//\nvar outputs = {\n response:{type:'property',\n event:function(prop){\n mods.output(mod,'response',prop)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n div.appendChild(document.createTextNode('objects (length):'))\n div.appendChild(document.createElement('br'))\n var text = document.createElement('textarea')\n text.setAttribute('rows',mods.ui.rows)\n text.setAttribute('cols',mods.ui.cols)\n text.addEventListener('input',function(evt) {\n format_string()\n })\n div.appendChild(text)\n mod.objects = text\n }\n//\n// local functions\n//\nfunction store_object(obj) {\n for (key in obj) {\n mod.library[key] = obj[key]\n }\n list_library()\n }\nfunction list_library() {\n var str = ''\n for (key in mod.library) {\n str += key+' ('+mod.library[key].length+')\\n'\n }\n mod.objects.value = str\n }\nfunction store_library() {\n mod.library['three.js'] = \n`\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (factory((global.THREE = {})));\n}(this, (function (exports) { 'use strict';\n\n // Polyfills\n\n if ( Number.EPSILON === undefined ) {\n\n Number.EPSILON = Math.pow( 2, - 52 );\n\n }\n\n if ( Number.isInteger === undefined ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger\n\n Number.isInteger = function ( value ) {\n\n return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value;\n\n };\n\n }\n\n //\n\n if ( Math.sign === undefined ) {\n\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign\n\n Math.sign = function ( x ) {\n\n return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;\n\n };\n\n }\n\n if ( 'name' in Function.prototype === false ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name\n\n Object.defineProperty( Function.prototype, 'name', {\n\n get: function () {\n\n return this.toString().match( /^\\s*function\\s*([^\\(\\s]*)/ )[ 1 ];\n\n }\n\n } );\n\n }\n\n if ( Object.assign === undefined ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n\n ( function () {\n\n Object.assign = function ( target ) {\n\n if ( target === undefined || target === null ) {\n\n throw new TypeError( 'Cannot convert undefined or null to object' );\n\n }\n\n var output = Object( target );\n\n for ( var index = 1; index < arguments.length; index ++ ) {\n\n var source = arguments[ index ];\n\n if ( source !== undefined && source !== null ) {\n\n for ( var nextKey in source ) {\n\n if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {\n\n output[ nextKey ] = source[ nextKey ];\n\n }\n\n }\n\n }\n\n }\n\n return output;\n\n };\n\n } )();\n\n }\n\n /**\n * https://github.com/mrdoob/eventdispatcher.js/\n */\n\n function EventDispatcher() {}\n\n Object.assign( EventDispatcher.prototype, {\n\n addEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) this._listeners = {};\n\n var listeners = this._listeners;\n\n if ( listeners[ type ] === undefined ) {\n\n listeners[ type ] = [];\n\n }\n\n if ( listeners[ type ].indexOf( listener ) === - 1 ) {\n\n listeners[ type ].push( listener );\n\n }\n\n },\n\n hasEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) return false;\n\n var listeners = this._listeners;\n\n return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;\n\n },\n\n removeEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) return;\n\n var listeners = this._listeners;\n var listenerArray = listeners[ type ];\n\n if ( listenerArray !== undefined ) {\n\n var index = listenerArray.indexOf( listener );\n\n if ( index !== - 1 ) {\n\n listenerArray.splice( index, 1 );\n\n }\n\n }\n\n },\n\n dispatchEvent: function ( event ) {\n\n if ( this._listeners === undefined ) return;\n\n var listeners = this._listeners;\n var listenerArray = listeners[ event.type ];\n\n if ( listenerArray !== undefined ) {\n\n event.target = this;\n\n var array = listenerArray.slice( 0 );\n\n for ( var i = 0, l = array.length; i < l; i ++ ) {\n\n array[ i ].call( this, event );\n\n }\n\n }\n\n }\n\n } );\n\n var REVISION = '91';\n var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };\n var CullFaceNone = 0;\n var CullFaceBack = 1;\n var CullFaceFront = 2;\n var CullFaceFrontBack = 3;\n var FrontFaceDirectionCW = 0;\n var FrontFaceDirectionCCW = 1;\n var BasicShadowMap = 0;\n var PCFShadowMap = 1;\n var PCFSoftShadowMap = 2;\n var FrontSide = 0;\n var BackSide = 1;\n var DoubleSide = 2;\n var FlatShading = 1;\n var SmoothShading = 2;\n var NoColors = 0;\n var FaceColors = 1;\n var VertexColors = 2;\n var NoBlending = 0;\n var NormalBlending = 1;\n var AdditiveBlending = 2;\n var SubtractiveBlending = 3;\n var MultiplyBlending = 4;\n var CustomBlending = 5;\n var AddEquation = 100;\n var SubtractEquation = 101;\n var ReverseSubtractEquation = 102;\n var MinEquation = 103;\n var MaxEquation = 104;\n var ZeroFactor = 200;\n var OneFactor = 201;\n var SrcColorFactor = 202;\n var OneMinusSrcColorFactor = 203;\n var SrcAlphaFactor = 204;\n var OneMinusSrcAlphaFactor = 205;\n var DstAlphaFactor = 206;\n var OneMinusDstAlphaFactor = 207;\n var DstColorFactor = 208;\n var OneMinusDstColorFactor = 209;\n var SrcAlphaSaturateFactor = 210;\n var NeverDepth = 0;\n var AlwaysDepth = 1;\n var LessDepth = 2;\n var LessEqualDepth = 3;\n var EqualDepth = 4;\n var GreaterEqualDepth = 5;\n var GreaterDepth = 6;\n var NotEqualDepth = 7;\n var MultiplyOperation = 0;\n var MixOperation = 1;\n var AddOperation = 2;\n var NoToneMapping = 0;\n var LinearToneMapping = 1;\n var ReinhardToneMapping = 2;\n var Uncharted2ToneMapping = 3;\n var CineonToneMapping = 4;\n var UVMapping = 300;\n var CubeReflectionMapping = 301;\n var CubeRefractionMapping = 302;\n var EquirectangularReflectionMapping = 303;\n var EquirectangularRefractionMapping = 304;\n var SphericalReflectionMapping = 305;\n var CubeUVReflectionMapping = 306;\n var CubeUVRefractionMapping = 307;\n var RepeatWrapping = 1000;\n var ClampToEdgeWrapping = 1001;\n var MirroredRepeatWrapping = 1002;\n var NearestFilter = 1003;\n var NearestMipMapNearestFilter = 1004;\n var NearestMipMapLinearFilter = 1005;\n var LinearFilter = 1006;\n var LinearMipMapNearestFilter = 1007;\n var LinearMipMapLinearFilter = 1008;\n var UnsignedByteType = 1009;\n var ByteType = 1010;\n var ShortType = 1011;\n var UnsignedShortType = 1012;\n var IntType = 1013;\n var UnsignedIntType = 1014;\n var FloatType = 1015;\n var HalfFloatType = 1016;\n var UnsignedShort4444Type = 1017;\n var UnsignedShort5551Type = 1018;\n var UnsignedShort565Type = 1019;\n var UnsignedInt248Type = 1020;\n var AlphaFormat = 1021;\n var RGBFormat = 1022;\n var RGBAFormat = 1023;\n var LuminanceFormat = 1024;\n var LuminanceAlphaFormat = 1025;\n var RGBEFormat = RGBAFormat;\n var DepthFormat = 1026;\n var DepthStencilFormat = 1027;\n var RGB_S3TC_DXT1_Format = 33776;\n var RGBA_S3TC_DXT1_Format = 33777;\n var RGBA_S3TC_DXT3_Format = 33778;\n var RGBA_S3TC_DXT5_Format = 33779;\n var RGB_PVRTC_4BPPV1_Format = 35840;\n var RGB_PVRTC_2BPPV1_Format = 35841;\n var RGBA_PVRTC_4BPPV1_Format = 35842;\n var RGBA_PVRTC_2BPPV1_Format = 35843;\n var RGB_ETC1_Format = 36196;\n var RGBA_ASTC_4x4_Format = 37808;\n var RGBA_ASTC_5x4_Format = 37809;\n var RGBA_ASTC_5x5_Format = 37810;\n var RGBA_ASTC_6x5_Format = 37811;\n var RGBA_ASTC_6x6_Format = 37812;\n var RGBA_ASTC_8x5_Format = 37813;\n var RGBA_ASTC_8x6_Format = 37814;\n var RGBA_ASTC_8x8_Format = 37815;\n var RGBA_ASTC_10x5_Format = 37816;\n var RGBA_ASTC_10x6_Format = 37817;\n var RGBA_ASTC_10x8_Format = 37818;\n var RGBA_ASTC_10x10_Format = 37819;\n var RGBA_ASTC_12x10_Format = 37820;\n var RGBA_ASTC_12x12_Format = 37821;\n var LoopOnce = 2200;\n var LoopRepeat = 2201;\n var LoopPingPong = 2202;\n var InterpolateDiscrete = 2300;\n var InterpolateLinear = 2301;\n var InterpolateSmooth = 2302;\n var ZeroCurvatureEnding = 2400;\n var ZeroSlopeEnding = 2401;\n var WrapAroundEnding = 2402;\n var TrianglesDrawMode = 0;\n var TriangleStripDrawMode = 1;\n var TriangleFanDrawMode = 2;\n var LinearEncoding = 3000;\n var sRGBEncoding = 3001;\n var GammaEncoding = 3007;\n var RGBEEncoding = 3002;\n var LogLuvEncoding = 3003;\n var RGBM7Encoding = 3004;\n var RGBM16Encoding = 3005;\n var RGBDEncoding = 3006;\n var BasicDepthPacking = 3200;\n var RGBADepthPacking = 3201;\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n var _Math = {\n\n DEG2RAD: Math.PI / 180,\n RAD2DEG: 180 / Math.PI,\n\n generateUUID: ( function () {\n\n // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136\n\n var lut = [];\n\n for ( var i = 0; i < 256; i ++ ) {\n\n lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ).toUpperCase();\n\n }\n\n return function generateUUID() {\n\n var d0 = Math.random() * 0xffffffff | 0;\n var d1 = Math.random() * 0xffffffff | 0;\n var d2 = Math.random() * 0xffffffff | 0;\n var d3 = Math.random() * 0xffffffff | 0;\n return lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +\n lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +\n lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +\n lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];\n\n };\n\n } )(),\n\n clamp: function ( value, min, max ) {\n\n return Math.max( min, Math.min( max, value ) );\n\n },\n\n // compute euclidian modulo of m % n\n // https://en.wikipedia.org/wiki/Modulo_operation\n\n euclideanModulo: function ( n, m ) {\n\n return ( ( n % m ) + m ) % m;\n\n },\n\n // Linear mapping from range <a1, a2> to range <b1, b2>\n\n mapLinear: function ( x, a1, a2, b1, b2 ) {\n\n return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );\n\n },\n\n // https://en.wikipedia.org/wiki/Linear_interpolation\n\n lerp: function ( x, y, t ) {\n\n return ( 1 - t ) * x + t * y;\n\n },\n\n // http://en.wikipedia.org/wiki/Smoothstep\n\n smoothstep: function ( x, min, max ) {\n\n if ( x <= min ) return 0;\n if ( x >= max ) return 1;\n\n x = ( x - min ) / ( max - min );\n\n return x * x * ( 3 - 2 * x );\n\n },\n\n smootherstep: function ( x, min, max ) {\n\n if ( x <= min ) return 0;\n if ( x >= max ) return 1;\n\n x = ( x - min ) / ( max - min );\n\n return x * x * x * ( x * ( x * 6 - 15 ) + 10 );\n\n },\n\n // Random integer from <low, high> interval\n\n randInt: function ( low, high ) {\n\n return low + Math.floor( Math.random() * ( high - low + 1 ) );\n\n },\n\n // Random float from <low, high> interval\n\n randFloat: function ( low, high ) {\n\n return low + Math.random() * ( high - low );\n\n },\n\n // Random float from <-range/2, range/2> interval\n\n randFloatSpread: function ( range ) {\n\n return range * ( 0.5 - Math.random() );\n\n },\n\n degToRad: function ( degrees ) {\n\n return degrees * _Math.DEG2RAD;\n\n },\n\n radToDeg: function ( radians ) {\n\n return radians * _Math.RAD2DEG;\n\n },\n\n isPowerOfTwo: function ( value ) {\n\n return ( value & ( value - 1 ) ) === 0 && value !== 0;\n\n },\n\n ceilPowerOfTwo: function ( value ) {\n\n return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );\n\n },\n\n floorPowerOfTwo: function ( value ) {\n\n return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author philogb / http://blog.thejit.org/\n * @author egraether / http://egraether.com/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n */\n\n function Vector2( x, y ) {\n\n this.x = x || 0;\n this.y = y || 0;\n\n }\n\n Object.defineProperties( Vector2.prototype, {\n\n \"width\": {\n\n get: function () {\n\n return this.x;\n\n },\n\n set: function ( value ) {\n\n this.x = value;\n\n }\n\n },\n\n \"height\": {\n\n get: function () {\n\n return this.y;\n\n },\n\n set: function ( value ) {\n\n this.y = value;\n\n }\n\n }\n\n } );\n\n Object.assign( Vector2.prototype, {\n\n isVector2: true,\n\n set: function ( x, y ) {\n\n this.x = x;\n this.y = y;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n\n return this;\n\n },\n\n multiply: function ( v ) {\n\n this.x *= v.x;\n this.y *= v.y;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n\n return this;\n\n },\n\n divide: function ( v ) {\n\n this.x /= v.x;\n this.y /= v.y;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n applyMatrix3: function ( m ) {\n\n var x = this.x, y = this.y;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];\n this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];\n\n return this;\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min = new Vector2();\n var max = new Vector2();\n\n return function clampScalar( minVal, maxVal ) {\n\n min.set( minVal, minVal );\n max.set( maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y;\n\n },\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n angle: function () {\n\n // computes the angle in radians with respect to the positive x-axis\n\n var angle = Math.atan2( this.y, this.x );\n\n if ( angle < 0 ) angle += 2 * Math.PI;\n\n return angle;\n\n },\n\n distanceTo: function ( v ) {\n\n return Math.sqrt( this.distanceToSquared( v ) );\n\n },\n\n distanceToSquared: function ( v ) {\n\n var dx = this.x - v.x, dy = this.y - v.y;\n return dx * dx + dy * dy;\n\n },\n\n manhattanDistanceTo: function ( v ) {\n\n return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n\n return this;\n\n },\n\n rotateAround: function ( center, angle ) {\n\n var c = Math.cos( angle ), s = Math.sin( angle );\n\n var x = this.x - center.x;\n var y = this.y - center.y;\n\n this.x = x * c - y * s + center.x;\n this.y = x * s + y * c + center.y;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author philogb / http://blog.thejit.org/\n * @author jordi_ros / http://plattsoft.com\n * @author D1plo1d / http://github.com/D1plo1d\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author timknip / http://www.floorplanner.com/\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Matrix4() {\n\n this.elements = [\n\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n ];\n\n if ( arguments.length > 0 ) {\n\n console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );\n\n }\n\n }\n\n Object.assign( Matrix4.prototype, {\n\n isMatrix4: true,\n\n set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n var te = this.elements;\n\n te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;\n te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;\n te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;\n te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;\n\n return this;\n\n },\n\n identity: function () {\n\n this.set(\n\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n clone: function () {\n\n return new Matrix4().fromArray( this.elements );\n\n },\n\n copy: function ( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];\n te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];\n te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];\n te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];\n\n return this;\n\n },\n\n copyPosition: function ( m ) {\n\n var te = this.elements, me = m.elements;\n\n te[ 12 ] = me[ 12 ];\n te[ 13 ] = me[ 13 ];\n te[ 14 ] = me[ 14 ];\n\n return this;\n\n },\n\n extractBasis: function ( xAxis, yAxis, zAxis ) {\n\n xAxis.setFromMatrixColumn( this, 0 );\n yAxis.setFromMatrixColumn( this, 1 );\n zAxis.setFromMatrixColumn( this, 2 );\n\n return this;\n\n },\n\n makeBasis: function ( xAxis, yAxis, zAxis ) {\n\n this.set(\n xAxis.x, yAxis.x, zAxis.x, 0,\n xAxis.y, yAxis.y, zAxis.y, 0,\n xAxis.z, yAxis.z, zAxis.z, 0,\n 0, 0, 0, 1\n );\n\n return this;\n\n },\n\n extractRotation: function () {\n\n var v1 = new Vector3();\n\n return function extractRotation( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();\n var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();\n var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();\n\n te[ 0 ] = me[ 0 ] * scaleX;\n te[ 1 ] = me[ 1 ] * scaleX;\n te[ 2 ] = me[ 2 ] * scaleX;\n\n te[ 4 ] = me[ 4 ] * scaleY;\n te[ 5 ] = me[ 5 ] * scaleY;\n te[ 6 ] = me[ 6 ] * scaleY;\n\n te[ 8 ] = me[ 8 ] * scaleZ;\n te[ 9 ] = me[ 9 ] * scaleZ;\n te[ 10 ] = me[ 10 ] * scaleZ;\n\n return this;\n\n };\n\n }(),\n\n makeRotationFromEuler: function ( euler ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );\n\n }\n\n var te = this.elements;\n\n var x = euler.x, y = euler.y, z = euler.z;\n var a = Math.cos( x ), b = Math.sin( x );\n var c = Math.cos( y ), d = Math.sin( y );\n var e = Math.cos( z ), f = Math.sin( z );\n\n if ( euler.order === 'XYZ' ) {\n\n var ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n te[ 0 ] = c * e;\n te[ 4 ] = - c * f;\n te[ 8 ] = d;\n\n te[ 1 ] = af + be * d;\n te[ 5 ] = ae - bf * d;\n te[ 9 ] = - b * c;\n\n te[ 2 ] = bf - ae * d;\n te[ 6 ] = be + af * d;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'YXZ' ) {\n\n var ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n te[ 0 ] = ce + df * b;\n te[ 4 ] = de * b - cf;\n te[ 8 ] = a * d;\n\n te[ 1 ] = a * f;\n te[ 5 ] = a * e;\n te[ 9 ] = - b;\n\n te[ 2 ] = cf * b - de;\n te[ 6 ] = df + ce * b;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'ZXY' ) {\n\n var ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n te[ 0 ] = ce - df * b;\n te[ 4 ] = - a * f;\n te[ 8 ] = de + cf * b;\n\n te[ 1 ] = cf + de * b;\n te[ 5 ] = a * e;\n te[ 9 ] = df - ce * b;\n\n te[ 2 ] = - a * d;\n te[ 6 ] = b;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'ZYX' ) {\n\n var ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n te[ 0 ] = c * e;\n te[ 4 ] = be * d - af;\n te[ 8 ] = ae * d + bf;\n\n te[ 1 ] = c * f;\n te[ 5 ] = bf * d + ae;\n te[ 9 ] = af * d - be;\n\n te[ 2 ] = - d;\n te[ 6 ] = b * c;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'YZX' ) {\n\n var ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n te[ 0 ] = c * e;\n te[ 4 ] = bd - ac * f;\n te[ 8 ] = bc * f + ad;\n\n te[ 1 ] = f;\n te[ 5 ] = a * e;\n te[ 9 ] = - b * e;\n\n te[ 2 ] = - d * e;\n te[ 6 ] = ad * f + bc;\n te[ 10 ] = ac - bd * f;\n\n } else if ( euler.order === 'XZY' ) {\n\n var ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n te[ 0 ] = c * e;\n te[ 4 ] = - f;\n te[ 8 ] = d * e;\n\n te[ 1 ] = ac * f + bd;\n te[ 5 ] = a * e;\n te[ 9 ] = ad * f - bc;\n\n te[ 2 ] = bc * f - ad;\n te[ 6 ] = b * e;\n te[ 10 ] = bd * f + ac;\n\n }\n\n // last column\n te[ 3 ] = 0;\n te[ 7 ] = 0;\n te[ 11 ] = 0;\n\n // bottom row\n te[ 12 ] = 0;\n te[ 13 ] = 0;\n te[ 14 ] = 0;\n te[ 15 ] = 1;\n\n return this;\n\n },\n\n makeRotationFromQuaternion: function ( q ) {\n\n var te = this.elements;\n\n var x = q._x, y = q._y, z = q._z, w = q._w;\n var x2 = x + x, y2 = y + y, z2 = z + z;\n var xx = x * x2, xy = x * y2, xz = x * z2;\n var yy = y * y2, yz = y * z2, zz = z * z2;\n var wx = w * x2, wy = w * y2, wz = w * z2;\n\n te[ 0 ] = 1 - ( yy + zz );\n te[ 4 ] = xy - wz;\n te[ 8 ] = xz + wy;\n\n te[ 1 ] = xy + wz;\n te[ 5 ] = 1 - ( xx + zz );\n te[ 9 ] = yz - wx;\n\n te[ 2 ] = xz - wy;\n te[ 6 ] = yz + wx;\n te[ 10 ] = 1 - ( xx + yy );\n\n // last column\n te[ 3 ] = 0;\n te[ 7 ] = 0;\n te[ 11 ] = 0;\n\n // bottom row\n te[ 12 ] = 0;\n te[ 13 ] = 0;\n te[ 14 ] = 0;\n te[ 15 ] = 1;\n\n return this;\n\n },\n\n lookAt: function () {\n\n var x = new Vector3();\n var y = new Vector3();\n var z = new Vector3();\n\n return function lookAt( eye, target, up ) {\n\n var te = this.elements;\n\n z.subVectors( eye, target );\n\n if ( z.lengthSq() === 0 ) {\n\n // eye and target are in the same position\n\n z.z = 1;\n\n }\n\n z.normalize();\n x.crossVectors( up, z );\n\n if ( x.lengthSq() === 0 ) {\n\n // up and z are parallel\n\n if ( Math.abs( up.z ) === 1 ) {\n\n z.x += 0.0001;\n\n } else {\n\n z.z += 0.0001;\n\n }\n\n z.normalize();\n x.crossVectors( up, z );\n\n }\n\n x.normalize();\n y.crossVectors( z, x );\n\n te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;\n te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;\n te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;\n\n return this;\n\n };\n\n }(),\n\n multiply: function ( m, n ) {\n\n if ( n !== undefined ) {\n\n console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );\n return this.multiplyMatrices( m, n );\n\n }\n\n return this.multiplyMatrices( this, m );\n\n },\n\n premultiply: function ( m ) {\n\n return this.multiplyMatrices( m, this );\n\n },\n\n multiplyMatrices: function ( a, b ) {\n\n var ae = a.elements;\n var be = b.elements;\n var te = this.elements;\n\n var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];\n var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];\n var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];\n var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];\n\n var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];\n var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];\n var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];\n var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];\n\n te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;\n te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;\n te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;\n te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;\n\n te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;\n te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;\n te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;\n te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;\n\n te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;\n te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;\n te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;\n te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;\n\n te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;\n te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;\n te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;\n te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n var te = this.elements;\n\n te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;\n te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;\n te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;\n te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;\n\n return this;\n\n },\n\n applyToBufferAttribute: function () {\n\n var v1 = new Vector3();\n\n return function applyToBufferAttribute( attribute ) {\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.x = attribute.getX( i );\n v1.y = attribute.getY( i );\n v1.z = attribute.getZ( i );\n\n v1.applyMatrix4( this );\n\n attribute.setXYZ( i, v1.x, v1.y, v1.z );\n\n }\n\n return attribute;\n\n };\n\n }(),\n\n determinant: function () {\n\n var te = this.elements;\n\n var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];\n var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];\n var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];\n var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];\n\n //TODO: make this more efficient\n //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )\n\n return (\n n41 * (\n + n14 * n23 * n32\n - n13 * n24 * n32\n - n14 * n22 * n33\n + n12 * n24 * n33\n + n13 * n22 * n34\n - n12 * n23 * n34\n ) +\n n42 * (\n + n11 * n23 * n34\n - n11 * n24 * n33\n + n14 * n21 * n33\n - n13 * n21 * n34\n + n13 * n24 * n31\n - n14 * n23 * n31\n ) +\n n43 * (\n + n11 * n24 * n32\n - n11 * n22 * n34\n - n14 * n21 * n32\n + n12 * n21 * n34\n + n14 * n22 * n31\n - n12 * n24 * n31\n ) +\n n44 * (\n - n13 * n22 * n31\n - n11 * n23 * n32\n + n11 * n22 * n33\n + n13 * n21 * n32\n - n12 * n21 * n33\n + n12 * n23 * n31\n )\n\n );\n\n },\n\n transpose: function () {\n\n var te = this.elements;\n var tmp;\n\n tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;\n tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;\n tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;\n\n tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;\n tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;\n tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;\n\n return this;\n\n },\n\n setPosition: function ( v ) {\n\n var te = this.elements;\n\n te[ 12 ] = v.x;\n te[ 13 ] = v.y;\n te[ 14 ] = v.z;\n\n return this;\n\n },\n\n getInverse: function ( m, throwOnDegenerate ) {\n\n // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n var te = this.elements,\n me = m.elements,\n\n n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],\n n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],\n n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],\n n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],\n\n t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;\n\n var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;\n\n if ( det === 0 ) {\n\n var msg = \"THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0\";\n\n if ( throwOnDegenerate === true ) {\n\n throw new Error( msg );\n\n } else {\n\n console.warn( msg );\n\n }\n\n return this.identity();\n\n }\n\n var detInv = 1 / det;\n\n te[ 0 ] = t11 * detInv;\n te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;\n te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;\n te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;\n\n te[ 4 ] = t12 * detInv;\n te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;\n te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;\n te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;\n\n te[ 8 ] = t13 * detInv;\n te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;\n te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;\n te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;\n\n te[ 12 ] = t14 * detInv;\n te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;\n te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;\n te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;\n\n return this;\n\n },\n\n scale: function ( v ) {\n\n var te = this.elements;\n var x = v.x, y = v.y, z = v.z;\n\n te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;\n te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;\n te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;\n te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;\n\n return this;\n\n },\n\n getMaxScaleOnAxis: function () {\n\n var te = this.elements;\n\n var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];\n var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];\n var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];\n\n return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );\n\n },\n\n makeTranslation: function ( x, y, z ) {\n\n this.set(\n\n 1, 0, 0, x,\n 0, 1, 0, y,\n 0, 0, 1, z,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationX: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n 1, 0, 0, 0,\n 0, c, - s, 0,\n 0, s, c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationY: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n c, 0, s, 0,\n 0, 1, 0, 0,\n - s, 0, c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationZ: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n c, - s, 0, 0,\n s, c, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationAxis: function ( axis, angle ) {\n\n // Based on http://www.gamedev.net/reference/articles/article1199.asp\n\n var c = Math.cos( angle );\n var s = Math.sin( angle );\n var t = 1 - c;\n var x = axis.x, y = axis.y, z = axis.z;\n var tx = t * x, ty = t * y;\n\n this.set(\n\n tx * x + c, tx * y - s * z, tx * z + s * y, 0,\n tx * y + s * z, ty * y + c, ty * z - s * x, 0,\n tx * z - s * y, ty * z + s * x, t * z * z + c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeScale: function ( x, y, z ) {\n\n this.set(\n\n x, 0, 0, 0,\n 0, y, 0, 0,\n 0, 0, z, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeShear: function ( x, y, z ) {\n\n this.set(\n\n 1, y, z, 0,\n x, 1, z, 0,\n x, y, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n compose: function ( position, quaternion, scale ) {\n\n this.makeRotationFromQuaternion( quaternion );\n this.scale( scale );\n this.setPosition( position );\n\n return this;\n\n },\n\n decompose: function () {\n\n var vector = new Vector3();\n var matrix = new Matrix4();\n\n return function decompose( position, quaternion, scale ) {\n\n var te = this.elements;\n\n var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();\n var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();\n var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();\n\n // if determine is negative, we need to invert one scale\n var det = this.determinant();\n if ( det < 0 ) sx = - sx;\n\n position.x = te[ 12 ];\n position.y = te[ 13 ];\n position.z = te[ 14 ];\n\n // scale the rotation part\n matrix.copy( this );\n\n var invSX = 1 / sx;\n var invSY = 1 / sy;\n var invSZ = 1 / sz;\n\n matrix.elements[ 0 ] *= invSX;\n matrix.elements[ 1 ] *= invSX;\n matrix.elements[ 2 ] *= invSX;\n\n matrix.elements[ 4 ] *= invSY;\n matrix.elements[ 5 ] *= invSY;\n matrix.elements[ 6 ] *= invSY;\n\n matrix.elements[ 8 ] *= invSZ;\n matrix.elements[ 9 ] *= invSZ;\n matrix.elements[ 10 ] *= invSZ;\n\n quaternion.setFromRotationMatrix( matrix );\n\n scale.x = sx;\n scale.y = sy;\n scale.z = sz;\n\n return this;\n\n };\n\n }(),\n\n makePerspective: function ( left, right, top, bottom, near, far ) {\n\n if ( far === undefined ) {\n\n console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );\n\n }\n\n var te = this.elements;\n var x = 2 * near / ( right - left );\n var y = 2 * near / ( top - bottom );\n\n var a = ( right + left ) / ( right - left );\n var b = ( top + bottom ) / ( top - bottom );\n var c = - ( far + near ) / ( far - near );\n var d = - 2 * far * near / ( far - near );\n\n te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;\n te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;\n te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;\n te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;\n\n return this;\n\n },\n\n makeOrthographic: function ( left, right, top, bottom, near, far ) {\n\n var te = this.elements;\n var w = 1.0 / ( right - left );\n var h = 1.0 / ( top - bottom );\n var p = 1.0 / ( far - near );\n\n var x = ( right + left ) * w;\n var y = ( top + bottom ) * h;\n var z = ( far + near ) * p;\n\n te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;\n te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;\n te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;\n te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;\n\n return this;\n\n },\n\n equals: function ( matrix ) {\n\n var te = this.elements;\n var me = matrix.elements;\n\n for ( var i = 0; i < 16; i ++ ) {\n\n if ( te[ i ] !== me[ i ] ) return false;\n\n }\n\n return true;\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n for ( var i = 0; i < 16; i ++ ) {\n\n this.elements[ i ] = array[ i + offset ];\n\n }\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n var te = this.elements;\n\n array[ offset ] = te[ 0 ];\n array[ offset + 1 ] = te[ 1 ];\n array[ offset + 2 ] = te[ 2 ];\n array[ offset + 3 ] = te[ 3 ];\n\n array[ offset + 4 ] = te[ 4 ];\n array[ offset + 5 ] = te[ 5 ];\n array[ offset + 6 ] = te[ 6 ];\n array[ offset + 7 ] = te[ 7 ];\n\n array[ offset + 8 ] = te[ 8 ];\n array[ offset + 9 ] = te[ 9 ];\n array[ offset + 10 ] = te[ 10 ];\n array[ offset + 11 ] = te[ 11 ];\n\n array[ offset + 12 ] = te[ 12 ];\n array[ offset + 13 ] = te[ 13 ];\n array[ offset + 14 ] = te[ 14 ];\n array[ offset + 15 ] = te[ 15 ];\n\n return array;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n */\n\n function Quaternion( x, y, z, w ) {\n\n this._x = x || 0;\n this._y = y || 0;\n this._z = z || 0;\n this._w = ( w !== undefined ) ? w : 1;\n\n }\n\n Object.assign( Quaternion, {\n\n slerp: function ( qa, qb, qm, t ) {\n\n return qm.copy( qa ).slerp( qb, t );\n\n },\n\n slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {\n\n // fuzz-free, array-based Quaternion SLERP operation\n\n var x0 = src0[ srcOffset0 + 0 ],\n y0 = src0[ srcOffset0 + 1 ],\n z0 = src0[ srcOffset0 + 2 ],\n w0 = src0[ srcOffset0 + 3 ],\n\n x1 = src1[ srcOffset1 + 0 ],\n y1 = src1[ srcOffset1 + 1 ],\n z1 = src1[ srcOffset1 + 2 ],\n w1 = src1[ srcOffset1 + 3 ];\n\n if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {\n\n var s = 1 - t,\n\n cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,\n\n dir = ( cos >= 0 ? 1 : - 1 ),\n sqrSin = 1 - cos * cos;\n\n // Skip the Slerp for tiny steps to avoid numeric problems:\n if ( sqrSin > Number.EPSILON ) {\n\n var sin = Math.sqrt( sqrSin ),\n len = Math.atan2( sin, cos * dir );\n\n s = Math.sin( s * len ) / sin;\n t = Math.sin( t * len ) / sin;\n\n }\n\n var tDir = t * dir;\n\n x0 = x0 * s + x1 * tDir;\n y0 = y0 * s + y1 * tDir;\n z0 = z0 * s + z1 * tDir;\n w0 = w0 * s + w1 * tDir;\n\n // Normalize in case we just did a lerp:\n if ( s === 1 - t ) {\n\n var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );\n\n x0 *= f;\n y0 *= f;\n z0 *= f;\n w0 *= f;\n\n }\n\n }\n\n dst[ dstOffset ] = x0;\n dst[ dstOffset + 1 ] = y0;\n dst[ dstOffset + 2 ] = z0;\n dst[ dstOffset + 3 ] = w0;\n\n }\n\n } );\n\n Object.defineProperties( Quaternion.prototype, {\n\n x: {\n\n get: function () {\n\n return this._x;\n\n },\n\n set: function ( value ) {\n\n this._x = value;\n this.onChangeCallback();\n\n }\n\n },\n\n y: {\n\n get: function () {\n\n return this._y;\n\n },\n\n set: function ( value ) {\n\n this._y = value;\n this.onChangeCallback();\n\n }\n\n },\n\n z: {\n\n get: function () {\n\n return this._z;\n\n },\n\n set: function ( value ) {\n\n this._z = value;\n this.onChangeCallback();\n\n }\n\n },\n\n w: {\n\n get: function () {\n\n return this._w;\n\n },\n\n set: function ( value ) {\n\n this._w = value;\n this.onChangeCallback();\n\n }\n\n }\n\n } );\n\n Object.assign( Quaternion.prototype, {\n\n set: function ( x, y, z, w ) {\n\n this._x = x;\n this._y = y;\n this._z = z;\n this._w = w;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this._x, this._y, this._z, this._w );\n\n },\n\n copy: function ( quaternion ) {\n\n this._x = quaternion.x;\n this._y = quaternion.y;\n this._z = quaternion.z;\n this._w = quaternion.w;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromEuler: function ( euler, update ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n }\n\n var x = euler._x, y = euler._y, z = euler._z, order = euler.order;\n\n // http://www.mathworks.com/matlabcentral/fileexchange/\n // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/\n // content/SpinCalc.m\n\n var cos = Math.cos;\n var sin = Math.sin;\n\n var c1 = cos( x / 2 );\n var c2 = cos( y / 2 );\n var c3 = cos( z / 2 );\n\n var s1 = sin( x / 2 );\n var s2 = sin( y / 2 );\n var s3 = sin( z / 2 );\n\n if ( order === 'XYZ' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'YXZ' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n } else if ( order === 'ZXY' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'ZYX' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n } else if ( order === 'YZX' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'XZY' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n }\n\n if ( update !== false ) this.onChangeCallback();\n\n return this;\n\n },\n\n setFromAxisAngle: function ( axis, angle ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n // assumes axis is normalized\n\n var halfAngle = angle / 2, s = Math.sin( halfAngle );\n\n this._x = axis.x * s;\n this._y = axis.y * s;\n this._z = axis.z * s;\n this._w = Math.cos( halfAngle );\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromRotationMatrix: function ( m ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var te = m.elements,\n\n m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],\n\n trace = m11 + m22 + m33,\n s;\n\n if ( trace > 0 ) {\n\n s = 0.5 / Math.sqrt( trace + 1.0 );\n\n this._w = 0.25 / s;\n this._x = ( m32 - m23 ) * s;\n this._y = ( m13 - m31 ) * s;\n this._z = ( m21 - m12 ) * s;\n\n } else if ( m11 > m22 && m11 > m33 ) {\n\n s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );\n\n this._w = ( m32 - m23 ) / s;\n this._x = 0.25 * s;\n this._y = ( m12 + m21 ) / s;\n this._z = ( m13 + m31 ) / s;\n\n } else if ( m22 > m33 ) {\n\n s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );\n\n this._w = ( m13 - m31 ) / s;\n this._x = ( m12 + m21 ) / s;\n this._y = 0.25 * s;\n this._z = ( m23 + m32 ) / s;\n\n } else {\n\n s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );\n\n this._w = ( m21 - m12 ) / s;\n this._x = ( m13 + m31 ) / s;\n this._y = ( m23 + m32 ) / s;\n this._z = 0.25 * s;\n\n }\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromUnitVectors: function () {\n\n // assumes direction vectors vFrom and vTo are normalized\n\n var v1 = new Vector3();\n var r;\n\n var EPS = 0.000001;\n\n return function setFromUnitVectors( vFrom, vTo ) {\n\n if ( v1 === undefined ) v1 = new Vector3();\n\n r = vFrom.dot( vTo ) + 1;\n\n if ( r < EPS ) {\n\n r = 0;\n\n if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {\n\n v1.set( - vFrom.y, vFrom.x, 0 );\n\n } else {\n\n v1.set( 0, - vFrom.z, vFrom.y );\n\n }\n\n } else {\n\n v1.crossVectors( vFrom, vTo );\n\n }\n\n this._x = v1.x;\n this._y = v1.y;\n this._z = v1.z;\n this._w = r;\n\n return this.normalize();\n\n };\n\n }(),\n\n inverse: function () {\n\n // quaternion is assumed to have unit length\n\n return this.conjugate();\n\n },\n\n conjugate: function () {\n\n this._x *= - 1;\n this._y *= - 1;\n this._z *= - 1;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;\n\n },\n\n lengthSq: function () {\n\n return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;\n\n },\n\n length: function () {\n\n return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );\n\n },\n\n normalize: function () {\n\n var l = this.length();\n\n if ( l === 0 ) {\n\n this._x = 0;\n this._y = 0;\n this._z = 0;\n this._w = 1;\n\n } else {\n\n l = 1 / l;\n\n this._x = this._x * l;\n this._y = this._y * l;\n this._z = this._z * l;\n this._w = this._w * l;\n\n }\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n multiply: function ( q, p ) {\n\n if ( p !== undefined ) {\n\n console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );\n return this.multiplyQuaternions( q, p );\n\n }\n\n return this.multiplyQuaternions( this, q );\n\n },\n\n premultiply: function ( q ) {\n\n return this.multiplyQuaternions( q, this );\n\n },\n\n multiplyQuaternions: function ( a, b ) {\n\n // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm\n\n var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;\n var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;\n\n this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;\n this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;\n this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;\n this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n slerp: function ( qb, t ) {\n\n if ( t === 0 ) return this;\n if ( t === 1 ) return this.copy( qb );\n\n var x = this._x, y = this._y, z = this._z, w = this._w;\n\n // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/\n\n var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;\n\n if ( cosHalfTheta < 0 ) {\n\n this._w = - qb._w;\n this._x = - qb._x;\n this._y = - qb._y;\n this._z = - qb._z;\n\n cosHalfTheta = - cosHalfTheta;\n\n } else {\n\n this.copy( qb );\n\n }\n\n if ( cosHalfTheta >= 1.0 ) {\n\n this._w = w;\n this._x = x;\n this._y = y;\n this._z = z;\n\n return this;\n\n }\n\n var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );\n\n if ( Math.abs( sinHalfTheta ) < 0.001 ) {\n\n this._w = 0.5 * ( w + this._w );\n this._x = 0.5 * ( x + this._x );\n this._y = 0.5 * ( y + this._y );\n this._z = 0.5 * ( z + this._z );\n\n return this;\n\n }\n\n var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );\n var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,\n ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;\n\n this._w = ( w * ratioA + this._w * ratioB );\n this._x = ( x * ratioA + this._x * ratioB );\n this._y = ( y * ratioA + this._y * ratioB );\n this._z = ( z * ratioA + this._z * ratioB );\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n equals: function ( quaternion ) {\n\n return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this._x = array[ offset ];\n this._y = array[ offset + 1 ];\n this._z = array[ offset + 2 ];\n this._w = array[ offset + 3 ];\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this._x;\n array[ offset + 1 ] = this._y;\n array[ offset + 2 ] = this._z;\n array[ offset + 3 ] = this._w;\n\n return array;\n\n },\n\n onChange: function ( callback ) {\n\n this.onChangeCallback = callback;\n\n return this;\n\n },\n\n onChangeCallback: function () {}\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author kile / http://kile.stravaganza.org/\n * @author philogb / http://blog.thejit.org/\n * @author mikael emtinger / http://gomo.se/\n * @author egraether / http://egraether.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Vector3( x, y, z ) {\n\n this.x = x || 0;\n this.y = y || 0;\n this.z = z || 0;\n\n }\n\n Object.assign( Vector3.prototype, {\n\n isVector3: true,\n\n set: function ( x, y, z ) {\n\n this.x = x;\n this.y = y;\n this.z = z;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n this.z = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setZ: function ( z ) {\n\n this.z = z;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n case 2: this.z = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n case 2: return this.z;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y, this.z );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n this.z = v.z;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n this.z += v.z;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n this.z += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n this.z = a.z + b.z;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n this.z += v.z * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n this.z -= v.z;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n this.z -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n this.z = a.z - b.z;\n\n return this;\n\n },\n\n multiply: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );\n return this.multiplyVectors( v, w );\n\n }\n\n this.x *= v.x;\n this.y *= v.y;\n this.z *= v.z;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n this.z *= scalar;\n\n return this;\n\n },\n\n multiplyVectors: function ( a, b ) {\n\n this.x = a.x * b.x;\n this.y = a.y * b.y;\n this.z = a.z * b.z;\n\n return this;\n\n },\n\n applyEuler: function () {\n\n var quaternion = new Quaternion();\n\n return function applyEuler( euler ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n }\n\n return this.applyQuaternion( quaternion.setFromEuler( euler ) );\n\n };\n\n }(),\n\n applyAxisAngle: function () {\n\n var quaternion = new Quaternion();\n\n return function applyAxisAngle( axis, angle ) {\n\n return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );\n\n };\n\n }(),\n\n applyMatrix3: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;\n this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;\n this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;\n\n return this;\n\n },\n\n applyMatrix4: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );\n\n this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;\n this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;\n this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;\n\n return this;\n\n },\n\n applyQuaternion: function ( q ) {\n\n var x = this.x, y = this.y, z = this.z;\n var qx = q.x, qy = q.y, qz = q.z, qw = q.w;\n\n // calculate quat * vector\n\n var ix = qw * x + qy * z - qz * y;\n var iy = qw * y + qz * x - qx * z;\n var iz = qw * z + qx * y - qy * x;\n var iw = - qx * x - qy * y - qz * z;\n\n // calculate result * inverse quat\n\n this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;\n this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;\n this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;\n\n return this;\n\n },\n\n project: function () {\n\n var matrix = new Matrix4();\n\n return function project( camera ) {\n\n matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );\n return this.applyMatrix4( matrix );\n\n };\n\n }(),\n\n unproject: function () {\n\n var matrix = new Matrix4();\n\n return function unproject( camera ) {\n\n matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );\n return this.applyMatrix4( matrix );\n\n };\n\n }(),\n\n transformDirection: function ( m ) {\n\n // input: THREE.Matrix4 affine matrix\n // vector interpreted as a direction\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;\n this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;\n this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;\n\n return this.normalize();\n\n },\n\n divide: function ( v ) {\n\n this.x /= v.x;\n this.y /= v.y;\n this.z /= v.z;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n this.z = Math.min( this.z, v.z );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n this.z = Math.max( this.z, v.z );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n this.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min = new Vector3();\n var max = new Vector3();\n\n return function clampScalar( minVal, maxVal ) {\n\n min.set( minVal, minVal, minVal );\n max.set( maxVal, maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n this.z = Math.floor( this.z );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n this.z = Math.ceil( this.z );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n this.z = Math.round( this.z );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n this.z = - this.z;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y + this.z * v.z;\n\n },\n\n // TODO lengthSquared?\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y + this.z * this.z;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n this.z += ( v.z - this.z ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n cross: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );\n return this.crossVectors( v, w );\n\n }\n\n return this.crossVectors( this, v );\n\n },\n\n crossVectors: function ( a, b ) {\n\n var ax = a.x, ay = a.y, az = a.z;\n var bx = b.x, by = b.y, bz = b.z;\n\n this.x = ay * bz - az * by;\n this.y = az * bx - ax * bz;\n this.z = ax * by - ay * bx;\n\n return this;\n\n },\n\n projectOnVector: function ( vector ) {\n\n var scalar = vector.dot( this ) / vector.lengthSq();\n\n return this.copy( vector ).multiplyScalar( scalar );\n\n },\n\n projectOnPlane: function () {\n\n var v1 = new Vector3();\n\n return function projectOnPlane( planeNormal ) {\n\n v1.copy( this ).projectOnVector( planeNormal );\n\n return this.sub( v1 );\n\n };\n\n }(),\n\n reflect: function () {\n\n // reflect incident vector off plane orthogonal to normal\n // normal is assumed to have unit length\n\n var v1 = new Vector3();\n\n return function reflect( normal ) {\n\n return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );\n\n };\n\n }(),\n\n angleTo: function ( v ) {\n\n var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );\n\n // clamp, to handle numerical problems\n\n return Math.acos( _Math.clamp( theta, - 1, 1 ) );\n\n },\n\n distanceTo: function ( v ) {\n\n return Math.sqrt( this.distanceToSquared( v ) );\n\n },\n\n distanceToSquared: function ( v ) {\n\n var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;\n\n return dx * dx + dy * dy + dz * dz;\n\n },\n\n manhattanDistanceTo: function ( v ) {\n\n return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );\n\n },\n\n setFromSpherical: function ( s ) {\n\n var sinPhiRadius = Math.sin( s.phi ) * s.radius;\n\n this.x = sinPhiRadius * Math.sin( s.theta );\n this.y = Math.cos( s.phi ) * s.radius;\n this.z = sinPhiRadius * Math.cos( s.theta );\n\n return this;\n\n },\n\n setFromCylindrical: function ( c ) {\n\n this.x = c.radius * Math.sin( c.theta );\n this.y = c.y;\n this.z = c.radius * Math.cos( c.theta );\n\n return this;\n\n },\n\n setFromMatrixPosition: function ( m ) {\n\n var e = m.elements;\n\n this.x = e[ 12 ];\n this.y = e[ 13 ];\n this.z = e[ 14 ];\n\n return this;\n\n },\n\n setFromMatrixScale: function ( m ) {\n\n var sx = this.setFromMatrixColumn( m, 0 ).length();\n var sy = this.setFromMatrixColumn( m, 1 ).length();\n var sz = this.setFromMatrixColumn( m, 2 ).length();\n\n this.x = sx;\n this.y = sy;\n this.z = sz;\n\n return this;\n\n },\n\n setFromMatrixColumn: function ( m, index ) {\n\n return this.fromArray( m.elements, index * 4 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n this.z = array[ offset + 2 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n array[ offset + 2 ] = this.z;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n this.z = attribute.getZ( index );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n * @author tschw\n */\n\n function Matrix3() {\n\n this.elements = [\n\n 1, 0, 0,\n 0, 1, 0,\n 0, 0, 1\n\n ];\n\n if ( arguments.length > 0 ) {\n\n console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );\n\n }\n\n }\n\n Object.assign( Matrix3.prototype, {\n\n isMatrix3: true,\n\n set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n var te = this.elements;\n\n te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;\n te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;\n te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;\n\n return this;\n\n },\n\n identity: function () {\n\n this.set(\n\n 1, 0, 0,\n 0, 1, 0,\n 0, 0, 1\n\n );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().fromArray( this.elements );\n\n },\n\n copy: function ( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];\n te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];\n te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];\n\n return this;\n\n },\n\n setFromMatrix4: function ( m ) {\n\n var me = m.elements;\n\n this.set(\n\n me[ 0 ], me[ 4 ], me[ 8 ],\n me[ 1 ], me[ 5 ], me[ 9 ],\n me[ 2 ], me[ 6 ], me[ 10 ]\n\n );\n\n return this;\n\n },\n\n applyToBufferAttribute: function () {\n\n var v1 = new Vector3();\n\n return function applyToBufferAttribute( attribute ) {\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.x = attribute.getX( i );\n v1.y = attribute.getY( i );\n v1.z = attribute.getZ( i );\n\n v1.applyMatrix3( this );\n\n attribute.setXYZ( i, v1.x, v1.y, v1.z );\n\n }\n\n return attribute;\n\n };\n\n }(),\n\n multiply: function ( m ) {\n\n return this.multiplyMatrices( this, m );\n\n },\n\n premultiply: function ( m ) {\n\n return this.multiplyMatrices( m, this );\n\n },\n\n multiplyMatrices: function ( a, b ) {\n\n var ae = a.elements;\n var be = b.elements;\n var te = this.elements;\n\n var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];\n var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];\n var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];\n\n var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];\n var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];\n var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];\n\n te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;\n te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;\n te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;\n\n te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;\n te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;\n te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;\n\n te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;\n te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;\n te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n var te = this.elements;\n\n te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;\n te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;\n te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;\n\n return this;\n\n },\n\n determinant: function () {\n\n var te = this.elements;\n\n var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],\n d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],\n g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];\n\n return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n\n },\n\n getInverse: function ( matrix, throwOnDegenerate ) {\n\n if ( matrix && matrix.isMatrix4 ) {\n\n console.error( \"THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument.\" );\n\n }\n\n var me = matrix.elements,\n te = this.elements,\n\n n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],\n n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],\n n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],\n\n t11 = n33 * n22 - n32 * n23,\n t12 = n32 * n13 - n33 * n12,\n t13 = n23 * n12 - n22 * n13,\n\n det = n11 * t11 + n21 * t12 + n31 * t13;\n\n if ( det === 0 ) {\n\n var msg = \"THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0\";\n\n if ( throwOnDegenerate === true ) {\n\n throw new Error( msg );\n\n } else {\n\n console.warn( msg );\n\n }\n\n return this.identity();\n\n }\n\n var detInv = 1 / det;\n\n te[ 0 ] = t11 * detInv;\n te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;\n te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;\n\n te[ 3 ] = t12 * detInv;\n te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;\n te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;\n\n te[ 6 ] = t13 * detInv;\n te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;\n te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;\n\n return this;\n\n },\n\n transpose: function () {\n\n var tmp, m = this.elements;\n\n tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;\n tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;\n tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;\n\n return this;\n\n },\n\n getNormalMatrix: function ( matrix4 ) {\n\n return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();\n\n },\n\n transposeIntoArray: function ( r ) {\n\n var m = this.elements;\n\n r[ 0 ] = m[ 0 ];\n r[ 1 ] = m[ 3 ];\n r[ 2 ] = m[ 6 ];\n r[ 3 ] = m[ 1 ];\n r[ 4 ] = m[ 4 ];\n r[ 5 ] = m[ 7 ];\n r[ 6 ] = m[ 2 ];\n r[ 7 ] = m[ 5 ];\n r[ 8 ] = m[ 8 ];\n\n return this;\n\n },\n\n setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {\n\n var c = Math.cos( rotation );\n var s = Math.sin( rotation );\n\n this.set(\n sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,\n - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,\n 0, 0, 1\n );\n\n },\n\n scale: function ( sx, sy ) {\n\n var te = this.elements;\n\n te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;\n te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;\n\n return this;\n\n },\n\n rotate: function ( theta ) {\n\n var c = Math.cos( theta );\n var s = Math.sin( theta );\n\n var te = this.elements;\n\n var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];\n var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];\n\n te[ 0 ] = c * a11 + s * a21;\n te[ 3 ] = c * a12 + s * a22;\n te[ 6 ] = c * a13 + s * a23;\n\n te[ 1 ] = - s * a11 + c * a21;\n te[ 4 ] = - s * a12 + c * a22;\n te[ 7 ] = - s * a13 + c * a23;\n\n return this;\n\n },\n\n translate: function ( tx, ty ) {\n\n var te = this.elements;\n\n te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];\n te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];\n\n return this;\n\n },\n\n equals: function ( matrix ) {\n\n var te = this.elements;\n var me = matrix.elements;\n\n for ( var i = 0; i < 9; i ++ ) {\n\n if ( te[ i ] !== me[ i ] ) return false;\n\n }\n\n return true;\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n for ( var i = 0; i < 9; i ++ ) {\n\n this.elements[ i ] = array[ i + offset ];\n\n }\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n var te = this.elements;\n\n array[ offset ] = te[ 0 ];\n array[ offset + 1 ] = te[ 1 ];\n array[ offset + 2 ] = te[ 2 ];\n\n array[ offset + 3 ] = te[ 3 ];\n array[ offset + 4 ] = te[ 4 ];\n array[ offset + 5 ] = te[ 5 ];\n\n array[ offset + 6 ] = te[ 6 ];\n array[ offset + 7 ] = te[ 7 ];\n array[ offset + 8 ] = te[ 8 ];\n\n return array;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author szimek / https://github.com/szimek/\n */\n\n var textureId = 0;\n\n function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n Object.defineProperty( this, 'id', { value: textureId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n\n this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;\n this.mipmaps = [];\n\n this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;\n\n this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;\n this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;\n\n this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;\n this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;\n\n this.anisotropy = anisotropy !== undefined ? anisotropy : 1;\n\n this.format = format !== undefined ? format : RGBAFormat;\n this.type = type !== undefined ? type : UnsignedByteType;\n\n this.offset = new Vector2( 0, 0 );\n this.repeat = new Vector2( 1, 1 );\n this.center = new Vector2( 0, 0 );\n this.rotation = 0;\n\n this.matrixAutoUpdate = true;\n this.matrix = new Matrix3();\n\n this.generateMipmaps = true;\n this.premultiplyAlpha = false;\n this.flipY = true;\n this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)\n\n // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.\n //\n // Also changing the encoding after already used by a Material will not automatically make the Material\n // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.\n this.encoding = encoding !== undefined ? encoding : LinearEncoding;\n\n this.version = 0;\n this.onUpdate = null;\n\n }\n\n Texture.DEFAULT_IMAGE = undefined;\n Texture.DEFAULT_MAPPING = UVMapping;\n\n Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Texture,\n\n isTexture: true,\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.name = source.name;\n\n this.image = source.image;\n this.mipmaps = source.mipmaps.slice( 0 );\n\n this.mapping = source.mapping;\n\n this.wrapS = source.wrapS;\n this.wrapT = source.wrapT;\n\n this.magFilter = source.magFilter;\n this.minFilter = source.minFilter;\n\n this.anisotropy = source.anisotropy;\n\n this.format = source.format;\n this.type = source.type;\n\n this.offset.copy( source.offset );\n this.repeat.copy( source.repeat );\n this.center.copy( source.center );\n this.rotation = source.rotation;\n\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n this.matrix.copy( source.matrix );\n\n this.generateMipmaps = source.generateMipmaps;\n this.premultiplyAlpha = source.premultiplyAlpha;\n this.flipY = source.flipY;\n this.unpackAlignment = source.unpackAlignment;\n this.encoding = source.encoding;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {\n\n return meta.textures[ this.uuid ];\n\n }\n\n function getDataURL( image ) {\n\n var canvas;\n\n if ( image instanceof HTMLCanvasElement ) {\n\n canvas = image;\n\n } else {\n\n canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = image.width;\n canvas.height = image.height;\n\n var context = canvas.getContext( '2d' );\n\n if ( image instanceof ImageData ) {\n\n context.putImageData( image, 0, 0 );\n\n } else {\n\n context.drawImage( image, 0, 0, image.width, image.height );\n\n }\n\n }\n\n if ( canvas.width > 2048 || canvas.height > 2048 ) {\n\n return canvas.toDataURL( 'image/jpeg', 0.6 );\n\n } else {\n\n return canvas.toDataURL( 'image/png' );\n\n }\n\n }\n\n var output = {\n\n metadata: {\n version: 4.5,\n type: 'Texture',\n generator: 'Texture.toJSON'\n },\n\n uuid: this.uuid,\n name: this.name,\n\n mapping: this.mapping,\n\n repeat: [ this.repeat.x, this.repeat.y ],\n offset: [ this.offset.x, this.offset.y ],\n center: [ this.center.x, this.center.y ],\n rotation: this.rotation,\n\n wrap: [ this.wrapS, this.wrapT ],\n\n format: this.format,\n minFilter: this.minFilter,\n magFilter: this.magFilter,\n anisotropy: this.anisotropy,\n\n flipY: this.flipY\n\n };\n\n if ( this.image !== undefined ) {\n\n // TODO: Move to THREE.Image\n\n var image = this.image;\n\n if ( image.uuid === undefined ) {\n\n image.uuid = _Math.generateUUID(); // UGH\n\n }\n\n if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {\n\n meta.images[ image.uuid ] = {\n uuid: image.uuid,\n url: getDataURL( image )\n };\n\n }\n\n output.image = image.uuid;\n\n }\n\n if ( ! isRootObject ) {\n\n meta.textures[ this.uuid ] = output;\n\n }\n\n return output;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n },\n\n transformUv: function ( uv ) {\n\n if ( this.mapping !== UVMapping ) return;\n\n uv.applyMatrix3( this.matrix );\n\n if ( uv.x < 0 || uv.x > 1 ) {\n\n switch ( this.wrapS ) {\n\n case RepeatWrapping:\n\n uv.x = uv.x - Math.floor( uv.x );\n break;\n\n case ClampToEdgeWrapping:\n\n uv.x = uv.x < 0 ? 0 : 1;\n break;\n\n case MirroredRepeatWrapping:\n\n if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {\n\n uv.x = Math.ceil( uv.x ) - uv.x;\n\n } else {\n\n uv.x = uv.x - Math.floor( uv.x );\n\n }\n break;\n\n }\n\n }\n\n if ( uv.y < 0 || uv.y > 1 ) {\n\n switch ( this.wrapT ) {\n\n case RepeatWrapping:\n\n uv.y = uv.y - Math.floor( uv.y );\n break;\n\n case ClampToEdgeWrapping:\n\n uv.y = uv.y < 0 ? 0 : 1;\n break;\n\n case MirroredRepeatWrapping:\n\n if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {\n\n uv.y = Math.ceil( uv.y ) - uv.y;\n\n } else {\n\n uv.y = uv.y - Math.floor( uv.y );\n\n }\n break;\n\n }\n\n }\n\n if ( this.flipY ) {\n\n uv.y = 1 - uv.y;\n\n }\n\n }\n\n } );\n\n Object.defineProperty( Texture.prototype, \"needsUpdate\", {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n /**\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author philogb / http://blog.thejit.org/\n * @author mikael emtinger / http://gomo.se/\n * @author egraether / http://egraether.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Vector4( x, y, z, w ) {\n\n this.x = x || 0;\n this.y = y || 0;\n this.z = z || 0;\n this.w = ( w !== undefined ) ? w : 1;\n\n }\n\n Object.assign( Vector4.prototype, {\n\n isVector4: true,\n\n set: function ( x, y, z, w ) {\n\n this.x = x;\n this.y = y;\n this.z = z;\n this.w = w;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n this.z = scalar;\n this.w = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setZ: function ( z ) {\n\n this.z = z;\n\n return this;\n\n },\n\n setW: function ( w ) {\n\n this.w = w;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n case 2: this.z = value; break;\n case 3: this.w = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n case 2: return this.z;\n case 3: return this.w;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y, this.z, this.w );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n this.z = v.z;\n this.w = ( v.w !== undefined ) ? v.w : 1;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n this.z += v.z;\n this.w += v.w;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n this.z += s;\n this.w += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n this.z = a.z + b.z;\n this.w = a.w + b.w;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n this.z += v.z * s;\n this.w += v.w * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n this.z -= v.z;\n this.w -= v.w;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n this.z -= s;\n this.w -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n this.z = a.z - b.z;\n this.w = a.w - b.w;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n this.z *= scalar;\n this.w *= scalar;\n\n return this;\n\n },\n\n applyMatrix4: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z, w = this.w;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;\n this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;\n this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;\n this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n setAxisAngleFromQuaternion: function ( q ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm\n\n // q is assumed to be normalized\n\n this.w = 2 * Math.acos( q.w );\n\n var s = Math.sqrt( 1 - q.w * q.w );\n\n if ( s < 0.0001 ) {\n\n this.x = 1;\n this.y = 0;\n this.z = 0;\n\n } else {\n\n this.x = q.x / s;\n this.y = q.y / s;\n this.z = q.z / s;\n\n }\n\n return this;\n\n },\n\n setAxisAngleFromRotationMatrix: function ( m ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var angle, x, y, z, // variables for result\n epsilon = 0.01, // margin to allow for rounding errors\n epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees\n\n te = m.elements,\n\n m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n if ( ( Math.abs( m12 - m21 ) < epsilon ) &&\n ( Math.abs( m13 - m31 ) < epsilon ) &&\n ( Math.abs( m23 - m32 ) < epsilon ) ) {\n\n // singularity found\n // first check for identity matrix which must have +1 for all terms\n // in leading diagonal and zero in other terms\n\n if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&\n ( Math.abs( m13 + m31 ) < epsilon2 ) &&\n ( Math.abs( m23 + m32 ) < epsilon2 ) &&\n ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {\n\n // this singularity is identity matrix so angle = 0\n\n this.set( 1, 0, 0, 0 );\n\n return this; // zero angle, arbitrary axis\n\n }\n\n // otherwise this singularity is angle = 180\n\n angle = Math.PI;\n\n var xx = ( m11 + 1 ) / 2;\n var yy = ( m22 + 1 ) / 2;\n var zz = ( m33 + 1 ) / 2;\n var xy = ( m12 + m21 ) / 4;\n var xz = ( m13 + m31 ) / 4;\n var yz = ( m23 + m32 ) / 4;\n\n if ( ( xx > yy ) && ( xx > zz ) ) {\n\n // m11 is the largest diagonal term\n\n if ( xx < epsilon ) {\n\n x = 0;\n y = 0.707106781;\n z = 0.707106781;\n\n } else {\n\n x = Math.sqrt( xx );\n y = xy / x;\n z = xz / x;\n\n }\n\n } else if ( yy > zz ) {\n\n // m22 is the largest diagonal term\n\n if ( yy < epsilon ) {\n\n x = 0.707106781;\n y = 0;\n z = 0.707106781;\n\n } else {\n\n y = Math.sqrt( yy );\n x = xy / y;\n z = yz / y;\n\n }\n\n } else {\n\n // m33 is the largest diagonal term so base result on this\n\n if ( zz < epsilon ) {\n\n x = 0.707106781;\n y = 0.707106781;\n z = 0;\n\n } else {\n\n z = Math.sqrt( zz );\n x = xz / z;\n y = yz / z;\n\n }\n\n }\n\n this.set( x, y, z, angle );\n\n return this; // return 180 deg rotation\n\n }\n\n // as we have reached here there are no singularities so we can handle normally\n\n var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +\n ( m13 - m31 ) * ( m13 - m31 ) +\n ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize\n\n if ( Math.abs( s ) < 0.001 ) s = 1;\n\n // prevent divide by zero, should not happen if matrix is orthogonal and should be\n // caught by singularity test above, but I've left it in just in case\n\n this.x = ( m32 - m23 ) / s;\n this.y = ( m13 - m31 ) / s;\n this.z = ( m21 - m12 ) / s;\n this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );\n\n return this;\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n this.z = Math.min( this.z, v.z );\n this.w = Math.min( this.w, v.w );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n this.z = Math.max( this.z, v.z );\n this.w = Math.max( this.w, v.w );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n this.z = Math.max( min.z, Math.min( max.z, this.z ) );\n this.w = Math.max( min.w, Math.min( max.w, this.w ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min, max;\n\n return function clampScalar( minVal, maxVal ) {\n\n if ( min === undefined ) {\n\n min = new Vector4();\n max = new Vector4();\n\n }\n\n min.set( minVal, minVal, minVal, minVal );\n max.set( maxVal, maxVal, maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n this.z = Math.floor( this.z );\n this.w = Math.floor( this.w );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n this.z = Math.ceil( this.z );\n this.w = Math.ceil( this.w );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n this.z = Math.round( this.z );\n this.w = Math.round( this.w );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n this.z = - this.z;\n this.w = - this.w;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;\n\n },\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n this.z += ( v.z - this.z ) * alpha;\n this.w += ( v.w - this.w ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n this.z = array[ offset + 2 ];\n this.w = array[ offset + 3 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n array[ offset + 2 ] = this.z;\n array[ offset + 3 ] = this.w;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n this.z = attribute.getZ( index );\n this.w = attribute.getW( index );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author szimek / https://github.com/szimek/\n * @author alteredq / http://alteredqualia.com/\n * @author Marius Kintel / https://github.com/kintel\n */\n\n /*\n In options, we can specify:\n * Texture parameters for an auto-generated target texture\n * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers\n */\n function WebGLRenderTarget( width, height, options ) {\n\n this.width = width;\n this.height = height;\n\n this.scissor = new Vector4( 0, 0, width, height );\n this.scissorTest = false;\n\n this.viewport = new Vector4( 0, 0, width, height );\n\n options = options || {};\n\n if ( options.minFilter === undefined ) options.minFilter = LinearFilter;\n\n this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );\n\n this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;\n this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;\n this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;\n\n }\n\n WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: WebGLRenderTarget,\n\n isWebGLRenderTarget: true,\n\n setSize: function ( width, height ) {\n\n if ( this.width !== width || this.height !== height ) {\n\n this.width = width;\n this.height = height;\n\n this.dispose();\n\n }\n\n this.viewport.set( 0, 0, width, height );\n this.scissor.set( 0, 0, width, height );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.width = source.width;\n this.height = source.height;\n\n this.viewport.copy( source.viewport );\n\n this.texture = source.texture.clone();\n\n this.depthBuffer = source.depthBuffer;\n this.stencilBuffer = source.stencilBuffer;\n this.depthTexture = source.depthTexture;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com\n */\n\n function WebGLRenderTargetCube( width, height, options ) {\n\n WebGLRenderTarget.call( this, width, height, options );\n\n this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5\n this.activeMipMapLevel = 0;\n\n }\n\n WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );\n WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;\n\n WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.image = { data: data, width: width, height: height };\n\n this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n this.generateMipmaps = false;\n this.flipY = false;\n this.unpackAlignment = 1;\n\n }\n\n DataTexture.prototype = Object.create( Texture.prototype );\n DataTexture.prototype.constructor = DataTexture;\n\n DataTexture.prototype.isDataTexture = true;\n\n /**\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Box3( min, max ) {\n\n this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );\n this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );\n\n }\n\n Object.assign( Box3.prototype, {\n\n isBox3: true,\n\n set: function ( min, max ) {\n\n this.min.copy( min );\n this.max.copy( max );\n\n return this;\n\n },\n\n setFromArray: function ( array ) {\n\n var minX = + Infinity;\n var minY = + Infinity;\n var minZ = + Infinity;\n\n var maxX = - Infinity;\n var maxY = - Infinity;\n var maxZ = - Infinity;\n\n for ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n var x = array[ i ];\n var y = array[ i + 1 ];\n var z = array[ i + 2 ];\n\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( z < minZ ) minZ = z;\n\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n if ( z > maxZ ) maxZ = z;\n\n }\n\n this.min.set( minX, minY, minZ );\n this.max.set( maxX, maxY, maxZ );\n\n return this;\n\n },\n\n setFromBufferAttribute: function ( attribute ) {\n\n var minX = + Infinity;\n var minY = + Infinity;\n var minZ = + Infinity;\n\n var maxX = - Infinity;\n var maxY = - Infinity;\n var maxZ = - Infinity;\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n var x = attribute.getX( i );\n var y = attribute.getY( i );\n var z = attribute.getZ( i );\n\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( z < minZ ) minZ = z;\n\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n if ( z > maxZ ) maxZ = z;\n\n }\n\n this.min.set( minX, minY, minZ );\n this.max.set( maxX, maxY, maxZ );\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.makeEmpty();\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n this.expandByPoint( points[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromCenterAndSize: function () {\n\n var v1 = new Vector3();\n\n return function setFromCenterAndSize( center, size ) {\n\n var halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n\n this.min.copy( center ).sub( halfSize );\n this.max.copy( center ).add( halfSize );\n\n return this;\n\n };\n\n }(),\n\n setFromObject: function ( object ) {\n\n this.makeEmpty();\n\n return this.expandByObject( object );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( box ) {\n\n this.min.copy( box.min );\n this.max.copy( box.max );\n\n return this;\n\n },\n\n makeEmpty: function () {\n\n this.min.x = this.min.y = this.min.z = + Infinity;\n this.max.x = this.max.y = this.max.z = - Infinity;\n\n return this;\n\n },\n\n isEmpty: function () {\n\n // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getCenter() target is now required' );\n target = new Vector3();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n },\n\n getSize: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getSize() target is now required' );\n target = new Vector3();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );\n\n },\n\n expandByPoint: function ( point ) {\n\n this.min.min( point );\n this.max.max( point );\n\n return this;\n\n },\n\n expandByVector: function ( vector ) {\n\n this.min.sub( vector );\n this.max.add( vector );\n\n return this;\n\n },\n\n expandByScalar: function ( scalar ) {\n\n this.min.addScalar( - scalar );\n this.max.addScalar( scalar );\n\n return this;\n\n },\n\n expandByObject: function () {\n\n // Computes the world-axis-aligned bounding box of an object (including its children),\n // accounting for both the object's, and children's, world transforms\n\n var scope, i, l;\n\n var v1 = new Vector3();\n\n function traverse( node ) {\n\n var geometry = node.geometry;\n\n if ( geometry !== undefined ) {\n\n if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n\n for ( i = 0, l = vertices.length; i < l; i ++ ) {\n\n v1.copy( vertices[ i ] );\n v1.applyMatrix4( node.matrixWorld );\n\n scope.expandByPoint( v1 );\n\n }\n\n } else if ( geometry.isBufferGeometry ) {\n\n var attribute = geometry.attributes.position;\n\n if ( attribute !== undefined ) {\n\n for ( i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );\n\n scope.expandByPoint( v1 );\n\n }\n\n }\n\n }\n\n }\n\n }\n\n return function expandByObject( object ) {\n\n scope = this;\n\n object.updateMatrixWorld( true );\n\n object.traverse( traverse );\n\n return this;\n\n };\n\n }(),\n\n containsPoint: function ( point ) {\n\n return point.x < this.min.x || point.x > this.max.x ||\n point.y < this.min.y || point.y > this.max.y ||\n point.z < this.min.z || point.z > this.max.z ? false : true;\n\n },\n\n containsBox: function ( box ) {\n\n return this.min.x <= box.min.x && box.max.x <= this.max.x &&\n this.min.y <= box.min.y && box.max.y <= this.max.y &&\n this.min.z <= box.min.z && box.max.z <= this.max.z;\n\n },\n\n getParameter: function ( point, target ) {\n\n // This can potentially have a divide by zero if the box\n // has a size dimension of 0.\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getParameter() target is now required' );\n target = new Vector3();\n\n }\n\n return target.set(\n ( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n ( point.y - this.min.y ) / ( this.max.y - this.min.y ),\n ( point.z - this.min.z ) / ( this.max.z - this.min.z )\n );\n\n },\n\n intersectsBox: function ( box ) {\n\n // using 6 splitting planes to rule out intersections.\n return box.max.x < this.min.x || box.min.x > this.max.x ||\n box.max.y < this.min.y || box.min.y > this.max.y ||\n box.max.z < this.min.z || box.min.z > this.max.z ? false : true;\n\n },\n\n intersectsSphere: ( function () {\n\n var closestPoint = new Vector3();\n\n return function intersectsSphere( sphere ) {\n\n // Find the point on the AABB closest to the sphere center.\n this.clampPoint( sphere.center, closestPoint );\n\n // If that point is inside the sphere, the AABB and sphere intersect.\n return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n };\n\n } )(),\n\n intersectsPlane: function ( plane ) {\n\n // We compute the minimum and maximum dot product values. If those values\n // are on the same side (back or front) of the plane, then there is no intersection.\n\n var min, max;\n\n if ( plane.normal.x > 0 ) {\n\n min = plane.normal.x * this.min.x;\n max = plane.normal.x * this.max.x;\n\n } else {\n\n min = plane.normal.x * this.max.x;\n max = plane.normal.x * this.min.x;\n\n }\n\n if ( plane.normal.y > 0 ) {\n\n min += plane.normal.y * this.min.y;\n max += plane.normal.y * this.max.y;\n\n } else {\n\n min += plane.normal.y * this.max.y;\n max += plane.normal.y * this.min.y;\n\n }\n\n if ( plane.normal.z > 0 ) {\n\n min += plane.normal.z * this.min.z;\n max += plane.normal.z * this.max.z;\n\n } else {\n\n min += plane.normal.z * this.max.z;\n max += plane.normal.z * this.min.z;\n\n }\n\n return ( min <= plane.constant && max >= plane.constant );\n\n },\n\n intersectsTriangle: ( function () {\n\n // triangle centered vertices\n var v0 = new Vector3();\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n // triangle edge vectors\n var f0 = new Vector3();\n var f1 = new Vector3();\n var f2 = new Vector3();\n\n var testAxis = new Vector3();\n\n var center = new Vector3();\n var extents = new Vector3();\n\n var triangleNormal = new Vector3();\n\n function satForAxes( axes ) {\n\n var i, j;\n\n for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) {\n\n testAxis.fromArray( axes, i );\n // project the aabb onto the seperating axis\n var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z );\n // project all 3 vertices of the triangle onto the seperating axis\n var p0 = v0.dot( testAxis );\n var p1 = v1.dot( testAxis );\n var p2 = v2.dot( testAxis );\n // actual test, basically see if either of the most extreme of the triangle points intersects r\n if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {\n\n // points of the projected triangle are outside the projected half-length of the aabb\n // the axis is seperating and we can exit\n return false;\n\n }\n\n }\n\n return true;\n\n }\n\n return function intersectsTriangle( triangle ) {\n\n if ( this.isEmpty() ) {\n\n return false;\n\n }\n\n // compute box center and extents\n this.getCenter( center );\n extents.subVectors( this.max, center );\n\n // translate triangle to aabb origin\n v0.subVectors( triangle.a, center );\n v1.subVectors( triangle.b, center );\n v2.subVectors( triangle.c, center );\n\n // compute edge vectors for triangle\n f0.subVectors( v1, v0 );\n f1.subVectors( v2, v1 );\n f2.subVectors( v0, v2 );\n\n // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb\n // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation\n // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)\n var axes = [\n 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y,\n f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x,\n - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0\n ];\n if ( ! satForAxes( axes ) ) {\n\n return false;\n\n }\n\n // test 3 face normals from the aabb\n axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];\n if ( ! satForAxes( axes ) ) {\n\n return false;\n\n }\n\n // finally testing the face normal of the triangle\n // use already existing triangle edge vectors here\n triangleNormal.crossVectors( f0, f1 );\n axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ];\n return satForAxes( axes );\n\n };\n\n } )(),\n\n clampPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .clampPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( point ).clamp( this.min, this.max );\n\n },\n\n distanceToPoint: function () {\n\n var v1 = new Vector3();\n\n return function distanceToPoint( point ) {\n\n var clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n return clampedPoint.sub( point ).length();\n\n };\n\n }(),\n\n getBoundingSphere: function () {\n\n var v1 = new Vector3();\n\n return function getBoundingSphere( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' );\n target = new Sphere();\n\n }\n\n this.getCenter( target.center );\n\n target.radius = this.getSize( v1 ).length() * 0.5;\n\n return target;\n\n };\n\n }(),\n\n intersect: function ( box ) {\n\n this.min.max( box.min );\n this.max.min( box.max );\n\n // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.\n if ( this.isEmpty() ) this.makeEmpty();\n\n return this;\n\n },\n\n union: function ( box ) {\n\n this.min.min( box.min );\n this.max.max( box.max );\n\n return this;\n\n },\n\n applyMatrix4: function () {\n\n var points = [\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3()\n ];\n\n return function applyMatrix4( matrix ) {\n\n // transform of empty box is an empty box.\n if ( this.isEmpty() ) return this;\n\n // NOTE: I am using a binary pattern to specify all 2^3 combinations below\n points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000\n points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001\n points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010\n points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011\n points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100\n points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101\n points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110\n points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111\n\n this.setFromPoints( points );\n\n return this;\n\n };\n\n }(),\n\n translate: function ( offset ) {\n\n this.min.add( offset );\n this.max.add( offset );\n\n return this;\n\n },\n\n equals: function ( box ) {\n\n return box.min.equals( this.min ) && box.max.equals( this.max );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Sphere( center, radius ) {\n\n this.center = ( center !== undefined ) ? center : new Vector3();\n this.radius = ( radius !== undefined ) ? radius : 0;\n\n }\n\n Object.assign( Sphere.prototype, {\n\n set: function ( center, radius ) {\n\n this.center.copy( center );\n this.radius = radius;\n\n return this;\n\n },\n\n setFromPoints: function () {\n\n var box = new Box3();\n\n return function setFromPoints( points, optionalCenter ) {\n\n var center = this.center;\n\n if ( optionalCenter !== undefined ) {\n\n center.copy( optionalCenter );\n\n } else {\n\n box.setFromPoints( points ).getCenter( center );\n\n }\n\n var maxRadiusSq = 0;\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );\n\n }\n\n this.radius = Math.sqrt( maxRadiusSq );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( sphere ) {\n\n this.center.copy( sphere.center );\n this.radius = sphere.radius;\n\n return this;\n\n },\n\n empty: function () {\n\n return ( this.radius <= 0 );\n\n },\n\n containsPoint: function ( point ) {\n\n return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );\n\n },\n\n distanceToPoint: function ( point ) {\n\n return ( point.distanceTo( this.center ) - this.radius );\n\n },\n\n intersectsSphere: function ( sphere ) {\n\n var radiusSum = this.radius + sphere.radius;\n\n return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsSphere( this );\n\n },\n\n intersectsPlane: function ( plane ) {\n\n return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;\n\n },\n\n clampPoint: function ( point, target ) {\n\n var deltaLengthSq = this.center.distanceToSquared( point );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Sphere: .clampPoint() target is now required' );\n target = new Vector3();\n\n }\n\n target.copy( point );\n\n if ( deltaLengthSq > ( this.radius * this.radius ) ) {\n\n target.sub( this.center ).normalize();\n target.multiplyScalar( this.radius ).add( this.center );\n\n }\n\n return target;\n\n },\n\n getBoundingBox: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );\n target = new Box3();\n\n }\n\n target.set( this.center, this.center );\n target.expandByScalar( this.radius );\n\n return target;\n\n },\n\n applyMatrix4: function ( matrix ) {\n\n this.center.applyMatrix4( matrix );\n this.radius = this.radius * matrix.getMaxScaleOnAxis();\n\n return this;\n\n },\n\n translate: function ( offset ) {\n\n this.center.add( offset );\n\n return this;\n\n },\n\n equals: function ( sphere ) {\n\n return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Plane( normal, constant ) {\n\n // normal is assumed to be normalized\n\n this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );\n this.constant = ( constant !== undefined ) ? constant : 0;\n\n }\n\n Object.assign( Plane.prototype, {\n\n set: function ( normal, constant ) {\n\n this.normal.copy( normal );\n this.constant = constant;\n\n return this;\n\n },\n\n setComponents: function ( x, y, z, w ) {\n\n this.normal.set( x, y, z );\n this.constant = w;\n\n return this;\n\n },\n\n setFromNormalAndCoplanarPoint: function ( normal, point ) {\n\n this.normal.copy( normal );\n this.constant = - point.dot( this.normal );\n\n return this;\n\n },\n\n setFromCoplanarPoints: function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function setFromCoplanarPoints( a, b, c ) {\n\n var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();\n\n // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?\n\n this.setFromNormalAndCoplanarPoint( normal, a );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( plane ) {\n\n this.normal.copy( plane.normal );\n this.constant = plane.constant;\n\n return this;\n\n },\n\n normalize: function () {\n\n // Note: will lead to a divide by zero if the plane is invalid.\n\n var inverseNormalLength = 1.0 / this.normal.length();\n this.normal.multiplyScalar( inverseNormalLength );\n this.constant *= inverseNormalLength;\n\n return this;\n\n },\n\n negate: function () {\n\n this.constant *= - 1;\n this.normal.negate();\n\n return this;\n\n },\n\n distanceToPoint: function ( point ) {\n\n return this.normal.dot( point ) + this.constant;\n\n },\n\n distanceToSphere: function ( sphere ) {\n\n return this.distanceToPoint( sphere.center ) - sphere.radius;\n\n },\n\n projectPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .projectPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );\n\n },\n\n intersectLine: function () {\n\n var v1 = new Vector3();\n\n return function intersectLine( line, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .intersectLine() target is now required' );\n target = new Vector3();\n\n }\n\n var direction = line.delta( v1 );\n\n var denominator = this.normal.dot( direction );\n\n if ( denominator === 0 ) {\n\n // line is coplanar, return origin\n if ( this.distanceToPoint( line.start ) === 0 ) {\n\n return target.copy( line.start );\n\n }\n\n // Unsure if this is the correct method to handle this case.\n return undefined;\n\n }\n\n var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;\n\n if ( t < 0 || t > 1 ) {\n\n return undefined;\n\n }\n\n return target.copy( direction ).multiplyScalar( t ).add( line.start );\n\n };\n\n }(),\n\n intersectsLine: function ( line ) {\n\n // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.\n\n var startSign = this.distanceToPoint( line.start );\n var endSign = this.distanceToPoint( line.end );\n\n return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsPlane( this );\n\n },\n\n intersectsSphere: function ( sphere ) {\n\n return sphere.intersectsPlane( this );\n\n },\n\n coplanarPoint: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.normal ).multiplyScalar( - this.constant );\n\n },\n\n applyMatrix4: function () {\n\n var v1 = new Vector3();\n var m1 = new Matrix3();\n\n return function applyMatrix4( matrix, optionalNormalMatrix ) {\n\n var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );\n\n var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );\n\n var normal = this.normal.applyMatrix3( normalMatrix ).normalize();\n\n this.constant = - referencePoint.dot( normal );\n\n return this;\n\n };\n\n }(),\n\n translate: function ( offset ) {\n\n this.constant -= offset.dot( this.normal );\n\n return this;\n\n },\n\n equals: function ( plane ) {\n\n return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author bhouston / http://clara.io\n */\n\n function Frustum( p0, p1, p2, p3, p4, p5 ) {\n\n this.planes = [\n\n ( p0 !== undefined ) ? p0 : new Plane(),\n ( p1 !== undefined ) ? p1 : new Plane(),\n ( p2 !== undefined ) ? p2 : new Plane(),\n ( p3 !== undefined ) ? p3 : new Plane(),\n ( p4 !== undefined ) ? p4 : new Plane(),\n ( p5 !== undefined ) ? p5 : new Plane()\n\n ];\n\n }\n\n Object.assign( Frustum.prototype, {\n\n set: function ( p0, p1, p2, p3, p4, p5 ) {\n\n var planes = this.planes;\n\n planes[ 0 ].copy( p0 );\n planes[ 1 ].copy( p1 );\n planes[ 2 ].copy( p2 );\n planes[ 3 ].copy( p3 );\n planes[ 4 ].copy( p4 );\n planes[ 5 ].copy( p5 );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( frustum ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n planes[ i ].copy( frustum.planes[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromMatrix: function ( m ) {\n\n var planes = this.planes;\n var me = m.elements;\n var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];\n var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];\n var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];\n var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];\n\n planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();\n planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();\n planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();\n planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();\n planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();\n planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();\n\n return this;\n\n },\n\n intersectsObject: function () {\n\n var sphere = new Sphere();\n\n return function intersectsObject( object ) {\n\n var geometry = object.geometry;\n\n if ( geometry.boundingSphere === null )\n geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere )\n .applyMatrix4( object.matrixWorld );\n\n return this.intersectsSphere( sphere );\n\n };\n\n }(),\n\n intersectsSprite: function () {\n\n var sphere = new Sphere();\n\n return function intersectsSprite( sprite ) {\n\n sphere.center.set( 0, 0, 0 );\n sphere.radius = 0.7071067811865476;\n sphere.applyMatrix4( sprite.matrixWorld );\n\n return this.intersectsSphere( sphere );\n\n };\n\n }(),\n\n intersectsSphere: function ( sphere ) {\n\n var planes = this.planes;\n var center = sphere.center;\n var negRadius = - sphere.radius;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n var distance = planes[ i ].distanceToPoint( center );\n\n if ( distance < negRadius ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n },\n\n intersectsBox: function () {\n\n var p1 = new Vector3(),\n p2 = new Vector3();\n\n return function intersectsBox( box ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n var plane = planes[ i ];\n\n p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;\n p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;\n p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;\n p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;\n p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;\n p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;\n\n var d1 = plane.distanceToPoint( p1 );\n var d2 = plane.distanceToPoint( p2 );\n\n // if both outside plane, no intersection\n\n if ( d1 < 0 && d2 < 0 ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n };\n\n }(),\n\n containsPoint: function ( point ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( planes[ i ].distanceToPoint( point ) < 0 ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n }\n\n } );\n\n var alphamap_fragment = \"#ifdef USE_ALPHAMAP\\n\\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\\n#endif\\n\";\n\n var alphamap_pars_fragment = \"#ifdef USE_ALPHAMAP\\n\\tuniform sampler2D alphaMap;\\n#endif\\n\";\n\n var alphatest_fragment = \"#ifdef ALPHATEST\\n\\tif ( diffuseColor.a < ALPHATEST ) discard;\\n#endif\\n\";\n\n var aomap_fragment = \"#ifdef USE_AOMAP\\n\\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\\n\\treflectedLight.indirectDiffuse *= ambientOcclusion;\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\\n\\t#endif\\n#endif\\n\";\n\n var aomap_pars_fragment = \"#ifdef USE_AOMAP\\n\\tuniform sampler2D aoMap;\\n\\tuniform float aoMapIntensity;\\n#endif\";\n\n var begin_vertex = \"\\nvec3 transformed = vec3( position );\\n\";\n\n var beginnormal_vertex = \"\\nvec3 objectNormal = vec3( normal );\\n\";\n\n var bsdfs = \"float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\\n\\tif( decayExponent > 0.0 ) {\\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\\n\\t\\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\\n\\t\\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\\n\\t\\treturn distanceFalloff * maxDistanceCutoffFactor;\\n#else\\n\\t\\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\\n#endif\\n\\t}\\n\\treturn 1.0;\\n}\\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\\n\\treturn RECIPROCAL_PI * diffuseColor;\\n}\\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\\n\\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\\n\\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\\n}\\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\treturn 1.0 / ( gl * gv );\\n}\\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\treturn 0.5 / max( gv + gl, EPSILON );\\n}\\nfloat D_GGX( const in float alpha, const in float dotNH ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\\n\\treturn RECIPROCAL_PI * a2 / pow2( denom );\\n}\\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat alpha = pow2( roughness );\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\\n\\tfloat D = D_GGX( alpha, dotNH );\\n\\treturn F * ( G * D );\\n}\\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\\n\\tconst float LUT_SIZE = 64.0;\\n\\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\\n\\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\\n\\tfloat dotNV = saturate( dot( N, V ) );\\n\\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\\n\\tuv = uv * LUT_SCALE + LUT_BIAS;\\n\\treturn uv;\\n}\\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\\n\\tfloat l = length( f );\\n\\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\\n}\\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\\n\\tfloat x = dot( v1, v2 );\\n\\tfloat y = abs( x );\\n\\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\\n\\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\\n\\tfloat v = a / b;\\n\\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\\n\\treturn cross( v1, v2 ) * theta_sintheta;\\n}\\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\\n\\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\\n\\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\\n\\tvec3 lightNormal = cross( v1, v2 );\\n\\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\\n\\tvec3 T1, T2;\\n\\tT1 = normalize( V - N * dot( V, N ) );\\n\\tT2 = - cross( N, T1 );\\n\\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\\n\\tvec3 coords[ 4 ];\\n\\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\\n\\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\\n\\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\\n\\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\\n\\tcoords[ 0 ] = normalize( coords[ 0 ] );\\n\\tcoords[ 1 ] = normalize( coords[ 1 ] );\\n\\tcoords[ 2 ] = normalize( coords[ 2 ] );\\n\\tcoords[ 3 ] = normalize( coords[ 3 ] );\\n\\tvec3 vectorFormFactor = vec3( 0.0 );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\\n\\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\\n\\treturn vec3( result );\\n}\\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\\n\\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\\n\\tvec4 r = roughness * c0 + c1;\\n\\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\\n\\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\\n\\treturn specularColor * AB.x + AB.y;\\n}\\nfloat G_BlinnPhong_Implicit( ) {\\n\\treturn 0.25;\\n}\\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\\n\\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\\n}\\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_BlinnPhong_Implicit( );\\n\\tfloat D = D_BlinnPhong( shininess, dotNH );\\n\\treturn F * ( G * D );\\n}\\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\\n\\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\\n}\\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\\n\\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\\n}\\n\";\n\n var bumpmap_pars_fragment = \"#ifdef USE_BUMPMAP\\n\\tuniform sampler2D bumpMap;\\n\\tuniform float bumpScale;\\n\\tvec2 dHdxy_fwd() {\\n\\t\\tvec2 dSTdx = dFdx( vUv );\\n\\t\\tvec2 dSTdy = dFdy( vUv );\\n\\t\\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\\n\\t\\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\\n\\t\\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\\n\\t\\treturn vec2( dBx, dBy );\\n\\t}\\n\\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\\n\\t\\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\\n\\t\\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\\n\\t\\tvec3 vN = surf_norm;\\n\\t\\tvec3 R1 = cross( vSigmaY, vN );\\n\\t\\tvec3 R2 = cross( vN, vSigmaX );\\n\\t\\tfloat fDet = dot( vSigmaX, R1 );\\n\\t\\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\\n\\t\\treturn normalize( abs( fDet ) * surf_norm - vGrad );\\n\\t}\\n#endif\\n\";\n\n var clipping_planes_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvec4 plane;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\\n\\t\\tplane = clippingPlanes[ i ];\\n\\t\\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\\n\\t}\\n\\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\\n\\t\\tbool clipped = true;\\n\\t\\t#pragma unroll_loop\\n\\t\\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\\n\\t\\t\\tplane = clippingPlanes[ i ];\\n\\t\\t\\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\\n\\t\\t}\\n\\t\\tif ( clipped ) discard;\\n\\t#endif\\n#endif\\n\";\n\n var clipping_planes_pars_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\t\\tvarying vec3 vViewPosition;\\n\\t#endif\\n\\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\\n#endif\\n\";\n\n var clipping_planes_pars_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n\";\n\n var clipping_planes_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n\";\n\n var color_fragment = \"#ifdef USE_COLOR\\n\\tdiffuseColor.rgb *= vColor;\\n#endif\";\n\n var color_pars_fragment = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\\n\";\n\n var color_pars_vertex = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\";\n\n var color_vertex = \"#ifdef USE_COLOR\\n\\tvColor.xyz = color.xyz;\\n#endif\";\n\n var common = \"#define PI 3.14159265359\\n#define PI2 6.28318530718\\n#define PI_HALF 1.5707963267949\\n#define RECIPROCAL_PI 0.31830988618\\n#define RECIPROCAL_PI2 0.15915494\\n#define LOG2 1.442695\\n#define EPSILON 1e-6\\n#define saturate(a) clamp( a, 0.0, 1.0 )\\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\\nfloat pow2( const in float x ) { return x*x; }\\nfloat pow3( const in float x ) { return x*x*x; }\\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\\nhighp float rand( const in vec2 uv ) {\\n\\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\\n\\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\\n\\treturn fract(sin(sn) * c);\\n}\\nstruct IncidentLight {\\n\\tvec3 color;\\n\\tvec3 direction;\\n\\tbool visible;\\n};\\nstruct ReflectedLight {\\n\\tvec3 directDiffuse;\\n\\tvec3 directSpecular;\\n\\tvec3 indirectDiffuse;\\n\\tvec3 indirectSpecular;\\n};\\nstruct GeometricContext {\\n\\tvec3 position;\\n\\tvec3 normal;\\n\\tvec3 viewDir;\\n};\\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\\n}\\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\\n}\\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\tfloat distance = dot( planeNormal, point - pointOnPlane );\\n\\treturn - distance * planeNormal + point;\\n}\\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn sign( dot( point - pointOnPlane, planeNormal ) );\\n}\\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\\n}\\nmat3 transposeMat3( const in mat3 m ) {\\n\\tmat3 tmp;\\n\\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\\n\\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\\n\\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\\n\\treturn tmp;\\n}\\nfloat linearToRelativeLuminance( const in vec3 color ) {\\n\\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\\n\\treturn dot( weights, color.rgb );\\n}\\n\";\n\n var cube_uv_reflection_fragment = \"#ifdef ENVMAP_TYPE_CUBE_UV\\n#define cubeUV_textureSize (1024.0)\\nint getFaceFromDirection(vec3 direction) {\\n\\tvec3 absDirection = abs(direction);\\n\\tint face = -1;\\n\\tif( absDirection.x > absDirection.z ) {\\n\\t\\tif(absDirection.x > absDirection.y )\\n\\t\\t\\tface = direction.x > 0.0 ? 0 : 3;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\telse {\\n\\t\\tif(absDirection.z > absDirection.y )\\n\\t\\t\\tface = direction.z > 0.0 ? 2 : 5;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\treturn face;\\n}\\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\\n\\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\\n\\tfloat dxRoughness = dFdx(roughness);\\n\\tfloat dyRoughness = dFdy(roughness);\\n\\tvec3 dx = dFdx( vec * scale * dxRoughness );\\n\\tvec3 dy = dFdy( vec * scale * dyRoughness );\\n\\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\\n\\td = clamp(d, 1.0, cubeUV_rangeClamp);\\n\\tfloat mipLevel = 0.5 * log2(d);\\n\\treturn vec2(floor(mipLevel), fract(mipLevel));\\n}\\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\\n\\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\\n\\tfloat a = 16.0 * cubeUV_rcpTextureSize;\\n\\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\\n\\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\\n\\tfloat powScale = exp2_packed.x * exp2_packed.y;\\n\\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\\n\\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\\n\\tbool bRes = mipLevel == 0.0;\\n\\tscale = bRes && (scale < a) ? a : scale;\\n\\tvec3 r;\\n\\tvec2 offset;\\n\\tint face = getFaceFromDirection(direction);\\n\\tfloat rcpPowScale = 1.0 / powScale;\\n\\tif( face == 0) {\\n\\t\\tr = vec3(direction.x, -direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 1) {\\n\\t\\tr = vec3(direction.y, direction.x, direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 2) {\\n\\t\\tr = vec3(direction.z, direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 3) {\\n\\t\\tr = vec3(direction.x, direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse if( face == 4) {\\n\\t\\tr = vec3(direction.y, direction.x, -direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse {\\n\\t\\tr = vec3(direction.z, -direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\tr = normalize(r);\\n\\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\\n\\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\\n\\tvec2 base = offset + vec2( texelOffset );\\n\\treturn base + s * ( scale - 2.0 * texelOffset );\\n}\\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\\n\\tfloat roughnessVal = roughness* cubeUV_maxLods3;\\n\\tfloat r1 = floor(roughnessVal);\\n\\tfloat r2 = r1 + 1.0;\\n\\tfloat t = fract(roughnessVal);\\n\\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\\n\\tfloat s = mipInfo.y;\\n\\tfloat level0 = mipInfo.x;\\n\\tfloat level1 = level0 + 1.0;\\n\\tlevel1 = level1 > 5.0 ? 5.0 : level1;\\n\\tlevel0 += min( floor( s + 0.5 ), 5.0 );\\n\\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\\n\\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\\n\\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\\n\\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\\n\\tvec4 result = mix(color10, color20, t);\\n\\treturn vec4(result.rgb, 1.0);\\n}\\n#endif\\n\";\n\n var defaultnormal_vertex = \"vec3 transformedNormal = normalMatrix * objectNormal;\\n#ifdef FLIP_SIDED\\n\\ttransformedNormal = - transformedNormal;\\n#endif\\n\";\n\n var displacementmap_pars_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\tuniform sampler2D displacementMap;\\n\\tuniform float displacementScale;\\n\\tuniform float displacementBias;\\n#endif\\n\";\n\n var displacementmap_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\\n#endif\\n\";\n\n var emissivemap_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\\n\\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\\n\\ttotalEmissiveRadiance *= emissiveColor.rgb;\\n#endif\\n\";\n\n var emissivemap_pars_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tuniform sampler2D emissiveMap;\\n#endif\\n\";\n\n var encodings_fragment = \" gl_FragColor = linearToOutputTexel( gl_FragColor );\\n\";\n\n var encodings_pars_fragment = \"\\nvec4 LinearToLinear( in vec4 value ) {\\n\\treturn value;\\n}\\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\\n\\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\\n}\\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\\n\\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\\n}\\nvec4 sRGBToLinear( in vec4 value ) {\\n\\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\\n}\\nvec4 LinearTosRGB( in vec4 value ) {\\n\\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\\n}\\nvec4 RGBEToLinear( in vec4 value ) {\\n\\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\\n}\\nvec4 LinearToRGBE( in vec4 value ) {\\n\\tfloat maxComponent = max( max( value.r, value.g ), value.b );\\n\\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\\n\\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\\n}\\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\\n\\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\\n}\\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\\n\\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\\n\\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\\n\\tM = ceil( M * 255.0 ) / 255.0;\\n\\treturn vec4( value.rgb / ( M * maxRange ), M );\\n}\\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\\n\\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\\n}\\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\\n\\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\\n\\tfloat D = max( maxRange / maxRGB, 1.0 );\\n\\tD = min( floor( D ) / 255.0, 1.0 );\\n\\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\\n}\\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\\nvec4 LinearToLogLuv( in vec4 value ) {\\n\\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\\n\\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\\n\\tvec4 vResult;\\n\\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\\n\\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\\n\\tvResult.w = fract(Le);\\n\\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\\n\\treturn vResult;\\n}\\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\\nvec4 LogLuvToLinear( in vec4 value ) {\\n\\tfloat Le = value.z * 255.0 + value.w;\\n\\tvec3 Xp_Y_XYZp;\\n\\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\\n\\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\\n\\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\\n\\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\\n\\treturn vec4( max(vRGB, 0.0), 1.0 );\\n}\\n\";\n\n var envmap_fragment = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#else\\n\\t\\tvec3 reflectVec = vReflect;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\\n\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\tvec2 sampleUV;\\n\\t\\treflectVec = normalize( reflectVec );\\n\\t\\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\t\\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\tvec4 envColor = texture2D( envMap, sampleUV );\\n\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\treflectVec = normalize( reflectVec );\\n\\t\\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\\n\\t\\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\\n\\t#else\\n\\t\\tvec4 envColor = vec4( 0.0 );\\n\\t#endif\\n\\tenvColor = envMapTexelToLinear( envColor );\\n\\t#ifdef ENVMAP_BLENDING_MULTIPLY\\n\\t\\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_MIX )\\n\\t\\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_ADD )\\n\\t\\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_pars_fragment = \"#if defined( USE_ENVMAP ) || defined( PHYSICAL )\\n\\tuniform float reflectivity;\\n\\tuniform float envMapIntensity;\\n#endif\\n#ifdef USE_ENVMAP\\n\\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tuniform samplerCube envMap;\\n\\t#else\\n\\t\\tuniform sampler2D envMap;\\n\\t#endif\\n\\tuniform float flipEnvMap;\\n\\tuniform int maxMipLevel;\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\\n\\t\\tuniform float refractionRatio;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_pars_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t\\tuniform float refractionRatio;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvWorldPosition = worldPosition.xyz;\\n\\t#else\\n\\t\\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvReflect = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#endif\\n#endif\\n\";\n\n var fog_vertex = \"\\n#ifdef USE_FOG\\nfogDepth = -mvPosition.z;\\n#endif\";\n\n var fog_pars_vertex = \"#ifdef USE_FOG\\n varying float fogDepth;\\n#endif\\n\";\n\n var fog_fragment = \"#ifdef USE_FOG\\n\\t#ifdef FOG_EXP2\\n\\t\\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\\n\\t#else\\n\\t\\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\\n\\t#endif\\n\\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\\n#endif\\n\";\n\n var fog_pars_fragment = \"#ifdef USE_FOG\\n\\tuniform vec3 fogColor;\\n\\tvarying float fogDepth;\\n\\t#ifdef FOG_EXP2\\n\\t\\tuniform float fogDensity;\\n\\t#else\\n\\t\\tuniform float fogNear;\\n\\t\\tuniform float fogFar;\\n\\t#endif\\n#endif\\n\";\n\n var gradientmap_pars_fragment = \"#ifdef TOON\\n\\tuniform sampler2D gradientMap;\\n\\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\\n\\t\\tfloat dotNL = dot( normal, lightDirection );\\n\\t\\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\\n\\t\\t#ifdef USE_GRADIENTMAP\\n\\t\\t\\treturn texture2D( gradientMap, coord ).rgb;\\n\\t\\t#else\\n\\t\\t\\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var lightmap_fragment = \"#ifdef USE_LIGHTMAP\\n\\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n#endif\\n\";\n\n var lightmap_pars_fragment = \"#ifdef USE_LIGHTMAP\\n\\tuniform sampler2D lightMap;\\n\\tuniform float lightMapIntensity;\\n#endif\";\n\n var lights_lambert_vertex = \"vec3 diffuse = vec3( 1.0 );\\nGeometricContext geometry;\\ngeometry.position = mvPosition.xyz;\\ngeometry.normal = normalize( transformedNormal );\\ngeometry.viewDir = normalize( -mvPosition.xyz );\\nGeometricContext backGeometry;\\nbackGeometry.position = geometry.position;\\nbackGeometry.normal = -geometry.normal;\\nbackGeometry.viewDir = geometry.viewDir;\\nvLightFront = vec3( 0.0 );\\n#ifdef DOUBLE_SIDED\\n\\tvLightBack = vec3( 0.0 );\\n#endif\\nIncidentLight directLight;\\nfloat dotNL;\\nvec3 directLightColor_Diffuse;\\n#if NUM_POINT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_DIR_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var lights_pars_begin = \"uniform vec3 ambientLightColor;\\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\\n\\tvec3 irradiance = ambientLightColor;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treturn irradiance;\\n}\\n#if NUM_DIR_LIGHTS > 0\\n\\tstruct DirectionalLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\\n\\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tdirectLight.color = directionalLight.color;\\n\\t\\tdirectLight.direction = directionalLight.direction;\\n\\t\\tdirectLight.visible = true;\\n\\t}\\n#endif\\n#if NUM_POINT_LIGHTS > 0\\n\\tstruct PointLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t\\tfloat shadowCameraNear;\\n\\t\\tfloat shadowCameraFar;\\n\\t};\\n\\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\\n\\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = pointLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tdirectLight.color = pointLight.color;\\n\\t\\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\\n\\t\\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\tstruct SpotLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tfloat coneCos;\\n\\t\\tfloat penumbraCos;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\\n\\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = spotLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tfloat angleCos = dot( directLight.direction, spotLight.direction );\\n\\t\\tif ( angleCos > spotLight.coneCos ) {\\n\\t\\t\\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\\n\\t\\t\\tdirectLight.color = spotLight.color;\\n\\t\\t\\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\\n\\t\\t\\tdirectLight.visible = true;\\n\\t\\t} else {\\n\\t\\t\\tdirectLight.color = vec3( 0.0 );\\n\\t\\t\\tdirectLight.visible = false;\\n\\t\\t}\\n\\t}\\n#endif\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tstruct RectAreaLight {\\n\\t\\tvec3 color;\\n\\t\\tvec3 position;\\n\\t\\tvec3 halfWidth;\\n\\t\\tvec3 halfHeight;\\n\\t};\\n\\tuniform sampler2D ltc_1;\\tuniform sampler2D ltc_2;\\n\\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\tstruct HemisphereLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 skyColor;\\n\\t\\tvec3 groundColor;\\n\\t};\\n\\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\\n\\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\\n\\t\\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\\n\\t\\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\\n\\t\\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tirradiance *= PI;\\n\\t\\t#endif\\n\\t\\treturn irradiance;\\n\\t}\\n#endif\\n\";\n\n var lights_pars_maps = \"#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\\n\\t\\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\\n\\t\\t#else\\n\\t\\t\\tvec4 envMapColor = vec4( 0.0 );\\n\\t\\t#endif\\n\\t\\treturn PI * envMapColor.rgb * envMapIntensity;\\n\\t}\\n\\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\tfloat maxMIPLevelScalar = float( maxMIPLevel );\\n\\t\\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\\n\\t\\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\\n\\t}\\n\\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\\n\\t\\t#endif\\n\\t\\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\\n\\t\\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\\n\\t\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\t\\tvec2 sampleUV;\\n\\t\\t\\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\t\\t\\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\t\\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#endif\\n\\t\\treturn envMapColor.rgb * envMapIntensity;\\n\\t}\\n#endif\\n\";\n\n var lights_phong_fragment = \"BlinnPhongMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\\nmaterial.specularColor = specular;\\nmaterial.specularShininess = shininess;\\nmaterial.specularStrength = specularStrength;\\n\";\n\n var lights_phong_pars_fragment = \"varying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\nstruct BlinnPhongMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tvec3\\tspecularColor;\\n\\tfloat\\tspecularShininess;\\n\\tfloat\\tspecularStrength;\\n};\\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t#ifdef TOON\\n\\t\\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\\n\\t#else\\n\\t\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\t\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#endif\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\\n}\\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_BlinnPhong\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_BlinnPhong\\n#define Material_LightProbeLOD( material )\\t(0)\\n\";\n\n var lights_physical_fragment = \"PhysicalMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\\n#ifdef STANDARD\\n\\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\\n#else\\n\\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\\n\\tmaterial.clearCoat = saturate( clearCoat );\\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\\n#endif\\n\";\n\n var lights_physical_pars_fragment = \"struct PhysicalMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tfloat\\tspecularRoughness;\\n\\tvec3\\tspecularColor;\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoat;\\n\\t\\tfloat clearCoatRoughness;\\n\\t#endif\\n};\\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\\n\\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\\n}\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t\\tvec3 normal = geometry.normal;\\n\\t\\tvec3 viewDir = geometry.viewDir;\\n\\t\\tvec3 position = geometry.position;\\n\\t\\tvec3 lightPos = rectAreaLight.position;\\n\\t\\tvec3 halfWidth = rectAreaLight.halfWidth;\\n\\t\\tvec3 halfHeight = rectAreaLight.halfHeight;\\n\\t\\tvec3 lightColor = rectAreaLight.color;\\n\\t\\tfloat roughness = material.specularRoughness;\\n\\t\\tvec3 rectCoords[ 4 ];\\n\\t\\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\\t\\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\\n\\t\\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\\n\\t\\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\\n\\t\\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\\n\\t\\tvec4 t1 = texture2D( ltc_1, uv );\\n\\t\\tvec4 t2 = texture2D( ltc_2, uv );\\n\\t\\tmat3 mInv = mat3(\\n\\t\\t\\tvec3( t1.x, 0, t1.y ),\\n\\t\\t\\tvec3( 0, 1, 0 ),\\n\\t\\t\\tvec3( t1.z, 0, t1.w )\\n\\t\\t);\\n\\t\\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\\n\\t\\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\\n\\t\\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\\n\\t}\\n#endif\\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\\n\\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t#ifndef STANDARD\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\tfloat dotNL = dotNV;\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Physical\\n#define RE_Direct_RectArea\\t\\tRE_Direct_RectArea_Physical\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Physical\\n#define RE_IndirectSpecular\\t\\tRE_IndirectSpecular_Physical\\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\\n\\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\\n}\\n\";\n\n var lights_fragment_begin = \"\\nGeometricContext geometry;\\ngeometry.position = - vViewPosition;\\ngeometry.normal = normal;\\ngeometry.viewDir = normalize( vViewPosition );\\nIncidentLight directLight;\\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tPointLight pointLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tSpotLight spotLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tDirectionalLight directionalLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\\n\\tRectAreaLight rectAreaLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\\n\\t\\trectAreaLight = rectAreaLights[ i ];\\n\\t\\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if defined( RE_IndirectDiffuse )\\n\\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\\n\\t#if ( NUM_HEMI_LIGHTS > 0 )\\n\\t\\t#pragma unroll_loop\\n\\t\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\t\\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t}\\n\\t#endif\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tvec3 radiance = vec3( 0.0 );\\n\\tvec3 clearCoatRadiance = vec3( 0.0 );\\n#endif\\n\";\n\n var lights_fragment_maps = \"#if defined( RE_IndirectDiffuse )\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tlightMapIrradiance *= PI;\\n\\t\\t#endif\\n\\t\\tirradiance += lightMapIrradiance;\\n\\t#endif\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\\n\\t#endif\\n#endif\\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\\n\\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\\n\\t#ifndef STANDARD\\n\\t\\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\\n\\t#endif\\n#endif\\n\";\n\n var lights_fragment_end = \"#if defined( RE_IndirectDiffuse )\\n\\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\\n#endif\\n\";\n\n var logdepthbuf_fragment = \"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\\n\\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\\n#endif\";\n\n var logdepthbuf_pars_fragment = \"#ifdef USE_LOGDEPTHBUF\\n\\tuniform float logDepthBufFC;\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n#endif\\n\";\n\n var logdepthbuf_pars_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n\\tuniform float logDepthBufFC;\\n#endif\";\n\n var logdepthbuf_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvFragDepth = 1.0 + gl_Position.w;\\n\\t#else\\n\\t\\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\\n\\t\\tgl_Position.z *= gl_Position.w;\\n\\t#endif\\n#endif\\n\";\n\n var map_fragment = \"#ifdef USE_MAP\\n\\tvec4 texelColor = texture2D( map, vUv );\\n\\ttexelColor = mapTexelToLinear( texelColor );\\n\\tdiffuseColor *= texelColor;\\n#endif\\n\";\n\n var map_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n var map_particle_fragment = \"#ifdef USE_MAP\\n\\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\\n\\tvec4 mapTexel = texture2D( map, uv );\\n\\tdiffuseColor *= mapTexelToLinear( mapTexel );\\n#endif\\n\";\n\n var map_particle_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform mat3 uvTransform;\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n var metalnessmap_fragment = \"float metalnessFactor = metalness;\\n#ifdef USE_METALNESSMAP\\n\\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\\n\\tmetalnessFactor *= texelMetalness.b;\\n#endif\\n\";\n\n var metalnessmap_pars_fragment = \"#ifdef USE_METALNESSMAP\\n\\tuniform sampler2D metalnessMap;\\n#endif\";\n\n var morphnormal_vertex = \"#ifdef USE_MORPHNORMALS\\n\\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\\n\\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\\n\\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\\n\\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\\n#endif\\n\";\n\n var morphtarget_pars_vertex = \"#ifdef USE_MORPHTARGETS\\n\\t#ifndef USE_MORPHNORMALS\\n\\tuniform float morphTargetInfluences[ 8 ];\\n\\t#else\\n\\tuniform float morphTargetInfluences[ 4 ];\\n\\t#endif\\n#endif\";\n\n var morphtarget_vertex = \"#ifdef USE_MORPHTARGETS\\n\\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\\n\\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\\n\\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\\n\\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\\n\\t#ifndef USE_MORPHNORMALS\\n\\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\\n\\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\\n\\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\\n\\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\\n\\t#endif\\n#endif\\n\";\n\n var normal_fragment_begin = \"#ifdef FLAT_SHADED\\n\\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\\n\\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\\n\\tvec3 normal = normalize( cross( fdx, fdy ) );\\n#else\\n\\tvec3 normal = normalize( vNormal );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\\n\\t#endif\\n#endif\\n\";\n\n var normal_fragment_maps = \"#ifdef USE_NORMALMAP\\n\\tnormal = perturbNormal2Arb( -vViewPosition, normal );\\n#elif defined( USE_BUMPMAP )\\n\\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\\n#endif\\n\";\n\n var normalmap_pars_fragment = \"#ifdef USE_NORMALMAP\\n\\tuniform sampler2D normalMap;\\n\\tuniform vec2 normalScale;\\n\\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\\n\\t\\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\\n\\t\\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\\n\\t\\tvec2 st0 = dFdx( vUv.st );\\n\\t\\tvec2 st1 = dFdy( vUv.st );\\n\\t\\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\\n\\t\\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\\n\\t\\tvec3 N = normalize( surf_norm );\\n\\t\\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\\n\\t\\tmapN.xy = normalScale * mapN.xy;\\n\\t\\tmat3 tsn = mat3( S, T, N );\\n\\t\\treturn normalize( tsn * mapN );\\n\\t}\\n#endif\\n\";\n\n var packing = \"vec3 packNormalToRGB( const in vec3 normal ) {\\n\\treturn normalize( normal ) * 0.5 + 0.5;\\n}\\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\\n\\treturn 2.0 * rgb.xyz - 1.0;\\n}\\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\\nconst float ShiftRight8 = 1. / 256.;\\nvec4 packDepthToRGBA( const in float v ) {\\n\\tvec4 r = vec4( fract( v * PackFactors ), v );\\n\\tr.yzw -= r.xyz * ShiftRight8;\\treturn r * PackUpscale;\\n}\\nfloat unpackRGBAToDepth( const in vec4 v ) {\\n\\treturn dot( v, UnpackFactors );\\n}\\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn ( viewZ + near ) / ( near - far );\\n}\\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\\n\\treturn linearClipZ * ( near - far ) - near;\\n}\\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\\n}\\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\\n\\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\\n}\\n\";\n\n var premultiplied_alpha_fragment = \"#ifdef PREMULTIPLIED_ALPHA\\n\\tgl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n\";\n\n var project_vertex = \"vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\\ngl_Position = projectionMatrix * mvPosition;\\n\";\n\n var dithering_fragment = \"#if defined( DITHERING )\\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\\n#endif\\n\";\n\n var dithering_pars_fragment = \"#if defined( DITHERING )\\n\\tvec3 dithering( vec3 color ) {\\n\\t\\tfloat grid_position = rand( gl_FragCoord.xy );\\n\\t\\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\\n\\t\\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\\n\\t\\treturn color + dither_shift_RGB;\\n\\t}\\n#endif\\n\";\n\n var roughnessmap_fragment = \"float roughnessFactor = roughness;\\n#ifdef USE_ROUGHNESSMAP\\n\\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\\n\\troughnessFactor *= texelRoughness.g;\\n#endif\\n\";\n\n var roughnessmap_pars_fragment = \"#ifdef USE_ROUGHNESSMAP\\n\\tuniform sampler2D roughnessMap;\\n#endif\";\n\n var shadowmap_pars_fragment = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n\\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\\n\\t\\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\\n\\t}\\n\\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\\n\\t\\tconst vec2 offset = vec2( 0.0, 1.0 );\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / size;\\n\\t\\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\\n\\t\\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\\n\\t\\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\\n\\t\\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\\n\\t\\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\\n\\t\\tvec2 f = fract( uv * size + 0.5 );\\n\\t\\tfloat a = mix( lb, lt, f.y );\\n\\t\\tfloat b = mix( rb, rt, f.y );\\n\\t\\tfloat c = mix( a, b, f.x );\\n\\t\\treturn c;\\n\\t}\\n\\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\\n\\t\\tfloat shadow = 1.0;\\n\\t\\tshadowCoord.xyz /= shadowCoord.w;\\n\\t\\tshadowCoord.z += shadowBias;\\n\\t\\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\\n\\t\\tbool inFrustum = all( inFrustumVec );\\n\\t\\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\\n\\t\\tbool frustumTest = all( frustumTestVec );\\n\\t\\tif ( frustumTest ) {\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\\n\\t\\t#endif\\n\\t\\t}\\n\\t\\treturn shadow;\\n\\t}\\n\\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\\n\\t\\tvec3 absV = abs( v );\\n\\t\\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\\n\\t\\tabsV *= scaleToCube;\\n\\t\\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\\n\\t\\tvec2 planar = v.xy;\\n\\t\\tfloat almostATexel = 1.5 * texelSizeY;\\n\\t\\tfloat almostOne = 1.0 - almostATexel;\\n\\t\\tif ( absV.z >= almostOne ) {\\n\\t\\t\\tif ( v.z > 0.0 )\\n\\t\\t\\t\\tplanar.x = 4.0 - v.x;\\n\\t\\t} else if ( absV.x >= almostOne ) {\\n\\t\\t\\tfloat signX = sign( v.x );\\n\\t\\t\\tplanar.x = v.z * signX + 2.0 * signX;\\n\\t\\t} else if ( absV.y >= almostOne ) {\\n\\t\\t\\tfloat signY = sign( v.y );\\n\\t\\t\\tplanar.x = v.x + 2.0 * signY + 2.0;\\n\\t\\t\\tplanar.y = v.z * signY - 2.0;\\n\\t\\t}\\n\\t\\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\\n\\t}\\n\\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\\n\\t\\tvec3 lightToPosition = shadowCoord.xyz;\\n\\t\\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\\t\\tdp += shadowBias;\\n\\t\\tvec3 bd3D = normalize( lightToPosition );\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var shadowmap_pars_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n#endif\\n\";\n\n var shadowmap_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n#endif\\n\";\n\n var shadowmask_pars_fragment = \"float getShadowMask() {\\n\\tfloat shadow = 1.0;\\n\\t#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\tDirectionalLight directionalLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\tSpotLight spotLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\tPointLight pointLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#endif\\n\\treturn shadow;\\n}\\n\";\n\n var skinbase_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\\n\\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\\n\\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\\n\\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\\n#endif\";\n\n var skinning_pars_vertex = \"#ifdef USE_SKINNING\\n\\tuniform mat4 bindMatrix;\\n\\tuniform mat4 bindMatrixInverse;\\n\\t#ifdef BONE_TEXTURE\\n\\t\\tuniform sampler2D boneTexture;\\n\\t\\tuniform int boneTextureSize;\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tfloat j = i * 4.0;\\n\\t\\t\\tfloat x = mod( j, float( boneTextureSize ) );\\n\\t\\t\\tfloat y = floor( j / float( boneTextureSize ) );\\n\\t\\t\\tfloat dx = 1.0 / float( boneTextureSize );\\n\\t\\t\\tfloat dy = 1.0 / float( boneTextureSize );\\n\\t\\t\\ty = dy * ( y + 0.5 );\\n\\t\\t\\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\\n\\t\\t\\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\\n\\t\\t\\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\\n\\t\\t\\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\\n\\t\\t\\tmat4 bone = mat4( v1, v2, v3, v4 );\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#else\\n\\t\\tuniform mat4 boneMatrices[ MAX_BONES ];\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tmat4 bone = boneMatrices[ int(i) ];\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#endif\\n#endif\\n\";\n\n var skinning_vertex = \"#ifdef USE_SKINNING\\n\\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\\n\\tvec4 skinned = vec4( 0.0 );\\n\\tskinned += boneMatX * skinVertex * skinWeight.x;\\n\\tskinned += boneMatY * skinVertex * skinWeight.y;\\n\\tskinned += boneMatZ * skinVertex * skinWeight.z;\\n\\tskinned += boneMatW * skinVertex * skinWeight.w;\\n\\ttransformed = ( bindMatrixInverse * skinned ).xyz;\\n#endif\\n\";\n\n var skinnormal_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 skinMatrix = mat4( 0.0 );\\n\\tskinMatrix += skinWeight.x * boneMatX;\\n\\tskinMatrix += skinWeight.y * boneMatY;\\n\\tskinMatrix += skinWeight.z * boneMatZ;\\n\\tskinMatrix += skinWeight.w * boneMatW;\\n\\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\\n\\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\\n#endif\\n\";\n\n var specularmap_fragment = \"float specularStrength;\\n#ifdef USE_SPECULARMAP\\n\\tvec4 texelSpecular = texture2D( specularMap, vUv );\\n\\tspecularStrength = texelSpecular.r;\\n#else\\n\\tspecularStrength = 1.0;\\n#endif\";\n\n var specularmap_pars_fragment = \"#ifdef USE_SPECULARMAP\\n\\tuniform sampler2D specularMap;\\n#endif\";\n\n var tonemapping_fragment = \"#if defined( TONE_MAPPING )\\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\\n#endif\\n\";\n\n var tonemapping_pars_fragment = \"#ifndef saturate\\n\\t#define saturate(a) clamp( a, 0.0, 1.0 )\\n#endif\\nuniform float toneMappingExposure;\\nuniform float toneMappingWhitePoint;\\nvec3 LinearToneMapping( vec3 color ) {\\n\\treturn toneMappingExposure * color;\\n}\\nvec3 ReinhardToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\treturn saturate( color / ( vec3( 1.0 ) + color ) );\\n}\\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\\nvec3 Uncharted2ToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\\n}\\nvec3 OptimizedCineonToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\tcolor = max( vec3( 0.0 ), color - 0.004 );\\n\\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\\n}\\n\";\n\n var uv_pars_fragment = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n#endif\";\n\n var uv_pars_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n\\tuniform mat3 uvTransform;\\n#endif\\n\";\n\n var uv_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\\n#endif\";\n\n var uv2_pars_fragment = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n var uv2_pars_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tattribute vec2 uv2;\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n var uv2_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvUv2 = uv2;\\n#endif\";\n\n var worldpos_vertex = \"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\\n\\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\\n#endif\\n\";\n\n var cube_frag = \"uniform samplerCube tCube;\\nuniform float tFlip;\\nuniform float opacity;\\nvarying vec3 vWorldPosition;\\nvoid main() {\\n\\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\\n\\tgl_FragColor.a *= opacity;\\n}\\n\";\n\n var cube_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\tgl_Position.z = gl_Position.w;\\n}\\n\";\n\n var depth_frag = \"#if DEPTH_PACKING == 3200\\n\\tuniform float opacity;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tdiffuseColor.a = opacity;\\n\\t#endif\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <logdepthbuf_fragment>\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\\n\\t#elif DEPTH_PACKING == 3201\\n\\t\\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\\n\\t#endif\\n}\\n\";\n\n var depth_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n}\\n\";\n\n var distanceRGBA_frag = \"#define DISTANCE\\nuniform vec3 referencePosition;\\nuniform float nearDistance;\\nuniform float farDistance;\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main () {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\tfloat dist = length( vWorldPosition - referencePosition );\\n\\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\\n\\tdist = saturate( dist );\\n\\tgl_FragColor = packDepthToRGBA( dist );\\n}\\n\";\n\n var distanceRGBA_vert = \"#define DISTANCE\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvWorldPosition = worldPosition.xyz;\\n}\\n\";\n\n var equirect_frag = \"uniform sampler2D tEquirect;\\nvarying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvec3 direction = normalize( vWorldPosition );\\n\\tvec2 sampleUV;\\n\\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\\n\\tgl_FragColor = texture2D( tEquirect, sampleUV );\\n}\\n\";\n\n var equirect_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n}\\n\";\n\n var linedashed_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\nuniform float dashSize;\\nuniform float totalSize;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\\n\\t\\tdiscard;\\n\\t}\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <color_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var linedashed_vert = \"uniform float scale;\\nattribute float lineDistance;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\tvLineDistance = scale * lineDistance;\\n\\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\\n\\tgl_Position = projectionMatrix * mvPosition;\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshbasic_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n\\t#else\\n\\t\\treflectedLight.indirectDiffuse += vec3( 1.0 );\\n\\t#endif\\n\\t#include <aomap_fragment>\\n\\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\\n\\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var meshbasic_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_ENVMAP\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshlambert_frag = \"uniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float opacity;\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <emissivemap_fragment>\\n\\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\\n\\t#include <lightmap_fragment>\\n\\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\\n\\t#else\\n\\t\\treflectedLight.directDiffuse = vLightFront;\\n\\t#endif\\n\\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshlambert_vert = \"#define LAMBERT\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <lights_lambert_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshphong_frag = \"#define PHONG\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform vec3 specular;\\nuniform float shininess;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <gradientmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <lights_phong_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_phong_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshphong_vert = \"#define PHONG\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshphysical_frag = \"#define PHYSICAL\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float roughness;\\nuniform float metalness;\\nuniform float opacity;\\n#ifndef STANDARD\\n\\tuniform float clearCoat;\\n\\tuniform float clearCoatRoughness;\\n#endif\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <cube_uv_reflection_fragment>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <lights_physical_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <roughnessmap_pars_fragment>\\n#include <metalnessmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <roughnessmap_fragment>\\n\\t#include <metalnessmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_physical_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshphysical_vert = \"#define PHYSICAL\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var normal_frag = \"#define NORMAL\\nuniform float opacity;\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\nvoid main() {\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\\n}\\n\";\n\n var normal_vert = \"#define NORMAL\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n}\\n\";\n\n var points_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <map_particle_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_particle_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphatest_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var points_vert = \"uniform float size;\\nuniform float scale;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#ifdef USE_SIZEATTENUATION\\n\\t\\tgl_PointSize = size * ( scale / - mvPosition.z );\\n\\t#else\\n\\t\\tgl_PointSize = size;\\n\\t#endif\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var shadow_frag = \"uniform vec3 color;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\nvoid main() {\\n\\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var shadow_vert = \"#include <fog_pars_vertex>\\n#include <shadowmap_pars_vertex>\\nvoid main() {\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var ShaderChunk = {\n alphamap_fragment: alphamap_fragment,\n alphamap_pars_fragment: alphamap_pars_fragment,\n alphatest_fragment: alphatest_fragment,\n aomap_fragment: aomap_fragment,\n aomap_pars_fragment: aomap_pars_fragment,\n begin_vertex: begin_vertex,\n beginnormal_vertex: beginnormal_vertex,\n bsdfs: bsdfs,\n bumpmap_pars_fragment: bumpmap_pars_fragment,\n clipping_planes_fragment: clipping_planes_fragment,\n clipping_planes_pars_fragment: clipping_planes_pars_fragment,\n clipping_planes_pars_vertex: clipping_planes_pars_vertex,\n clipping_planes_vertex: clipping_planes_vertex,\n color_fragment: color_fragment,\n color_pars_fragment: color_pars_fragment,\n color_pars_vertex: color_pars_vertex,\n color_vertex: color_vertex,\n common: common,\n cube_uv_reflection_fragment: cube_uv_reflection_fragment,\n defaultnormal_vertex: defaultnormal_vertex,\n displacementmap_pars_vertex: displacementmap_pars_vertex,\n displacementmap_vertex: displacementmap_vertex,\n emissivemap_fragment: emissivemap_fragment,\n emissivemap_pars_fragment: emissivemap_pars_fragment,\n encodings_fragment: encodings_fragment,\n encodings_pars_fragment: encodings_pars_fragment,\n envmap_fragment: envmap_fragment,\n envmap_pars_fragment: envmap_pars_fragment,\n envmap_pars_vertex: envmap_pars_vertex,\n envmap_vertex: envmap_vertex,\n fog_vertex: fog_vertex,\n fog_pars_vertex: fog_pars_vertex,\n fog_fragment: fog_fragment,\n fog_pars_fragment: fog_pars_fragment,\n gradientmap_pars_fragment: gradientmap_pars_fragment,\n lightmap_fragment: lightmap_fragment,\n lightmap_pars_fragment: lightmap_pars_fragment,\n lights_lambert_vertex: lights_lambert_vertex,\n lights_pars_begin: lights_pars_begin,\n lights_pars_maps: lights_pars_maps,\n lights_phong_fragment: lights_phong_fragment,\n lights_phong_pars_fragment: lights_phong_pars_fragment,\n lights_physical_fragment: lights_physical_fragment,\n lights_physical_pars_fragment: lights_physical_pars_fragment,\n lights_fragment_begin: lights_fragment_begin,\n lights_fragment_maps: lights_fragment_maps,\n lights_fragment_end: lights_fragment_end,\n logdepthbuf_fragment: logdepthbuf_fragment,\n logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,\n logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,\n logdepthbuf_vertex: logdepthbuf_vertex,\n map_fragment: map_fragment,\n map_pars_fragment: map_pars_fragment,\n map_particle_fragment: map_particle_fragment,\n map_particle_pars_fragment: map_particle_pars_fragment,\n metalnessmap_fragment: metalnessmap_fragment,\n metalnessmap_pars_fragment: metalnessmap_pars_fragment,\n morphnormal_vertex: morphnormal_vertex,\n morphtarget_pars_vertex: morphtarget_pars_vertex,\n morphtarget_vertex: morphtarget_vertex,\n normal_fragment_begin: normal_fragment_begin,\n normal_fragment_maps: normal_fragment_maps,\n normalmap_pars_fragment: normalmap_pars_fragment,\n packing: packing,\n premultiplied_alpha_fragment: premultiplied_alpha_fragment,\n project_vertex: project_vertex,\n dithering_fragment: dithering_fragment,\n dithering_pars_fragment: dithering_pars_fragment,\n roughnessmap_fragment: roughnessmap_fragment,\n roughnessmap_pars_fragment: roughnessmap_pars_fragment,\n shadowmap_pars_fragment: shadowmap_pars_fragment,\n shadowmap_pars_vertex: shadowmap_pars_vertex,\n shadowmap_vertex: shadowmap_vertex,\n shadowmask_pars_fragment: shadowmask_pars_fragment,\n skinbase_vertex: skinbase_vertex,\n skinning_pars_vertex: skinning_pars_vertex,\n skinning_vertex: skinning_vertex,\n skinnormal_vertex: skinnormal_vertex,\n specularmap_fragment: specularmap_fragment,\n specularmap_pars_fragment: specularmap_pars_fragment,\n tonemapping_fragment: tonemapping_fragment,\n tonemapping_pars_fragment: tonemapping_pars_fragment,\n uv_pars_fragment: uv_pars_fragment,\n uv_pars_vertex: uv_pars_vertex,\n uv_vertex: uv_vertex,\n uv2_pars_fragment: uv2_pars_fragment,\n uv2_pars_vertex: uv2_pars_vertex,\n uv2_vertex: uv2_vertex,\n worldpos_vertex: worldpos_vertex,\n\n cube_frag: cube_frag,\n cube_vert: cube_vert,\n depth_frag: depth_frag,\n depth_vert: depth_vert,\n distanceRGBA_frag: distanceRGBA_frag,\n distanceRGBA_vert: distanceRGBA_vert,\n equirect_frag: equirect_frag,\n equirect_vert: equirect_vert,\n linedashed_frag: linedashed_frag,\n linedashed_vert: linedashed_vert,\n meshbasic_frag: meshbasic_frag,\n meshbasic_vert: meshbasic_vert,\n meshlambert_frag: meshlambert_frag,\n meshlambert_vert: meshlambert_vert,\n meshphong_frag: meshphong_frag,\n meshphong_vert: meshphong_vert,\n meshphysical_frag: meshphysical_frag,\n meshphysical_vert: meshphysical_vert,\n normal_frag: normal_frag,\n normal_vert: normal_vert,\n points_frag: points_frag,\n points_vert: points_vert,\n shadow_frag: shadow_frag,\n shadow_vert: shadow_vert\n };\n\n /**\n * Uniform Utilities\n */\n\n var UniformsUtils = {\n\n merge: function ( uniforms ) {\n\n var merged = {};\n\n for ( var u = 0; u < uniforms.length; u ++ ) {\n\n var tmp = this.clone( uniforms[ u ] );\n\n for ( var p in tmp ) {\n\n merged[ p ] = tmp[ p ];\n\n }\n\n }\n\n return merged;\n\n },\n\n clone: function ( uniforms_src ) {\n\n var uniforms_dst = {};\n\n for ( var u in uniforms_src ) {\n\n uniforms_dst[ u ] = {};\n\n for ( var p in uniforms_src[ u ] ) {\n\n var parameter_src = uniforms_src[ u ][ p ];\n\n if ( parameter_src && ( parameter_src.isColor ||\n parameter_src.isMatrix3 || parameter_src.isMatrix4 ||\n parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||\n parameter_src.isTexture ) ) {\n\n uniforms_dst[ u ][ p ] = parameter_src.clone();\n\n } else if ( Array.isArray( parameter_src ) ) {\n\n uniforms_dst[ u ][ p ] = parameter_src.slice();\n\n } else {\n\n uniforms_dst[ u ][ p ] = parameter_src;\n\n }\n\n }\n\n }\n\n return uniforms_dst;\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,\n 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,\n 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,\n 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,\n 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,\n 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,\n 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,\n 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,\n 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,\n 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,\n 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,\n 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,\n 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,\n 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,\n 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,\n 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,\n 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,\n 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,\n 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,\n 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,\n 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,\n 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,\n 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,\n 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };\n\n function Color( r, g, b ) {\n\n if ( g === undefined && b === undefined ) {\n\n // r is THREE.Color, hex or string\n return this.set( r );\n\n }\n\n return this.setRGB( r, g, b );\n\n }\n\n Object.assign( Color.prototype, {\n\n isColor: true,\n\n r: 1, g: 1, b: 1,\n\n set: function ( value ) {\n\n if ( value && value.isColor ) {\n\n this.copy( value );\n\n } else if ( typeof value === 'number' ) {\n\n this.setHex( value );\n\n } else if ( typeof value === 'string' ) {\n\n this.setStyle( value );\n\n }\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.r = scalar;\n this.g = scalar;\n this.b = scalar;\n\n return this;\n\n },\n\n setHex: function ( hex ) {\n\n hex = Math.floor( hex );\n\n this.r = ( hex >> 16 & 255 ) / 255;\n this.g = ( hex >> 8 & 255 ) / 255;\n this.b = ( hex & 255 ) / 255;\n\n return this;\n\n },\n\n setRGB: function ( r, g, b ) {\n\n this.r = r;\n this.g = g;\n this.b = b;\n\n return this;\n\n },\n\n setHSL: function () {\n\n function hue2rgb( p, q, t ) {\n\n if ( t < 0 ) t += 1;\n if ( t > 1 ) t -= 1;\n if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;\n if ( t < 1 / 2 ) return q;\n if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );\n return p;\n\n }\n\n return function setHSL( h, s, l ) {\n\n // h,s,l ranges are in 0.0 - 1.0\n h = _Math.euclideanModulo( h, 1 );\n s = _Math.clamp( s, 0, 1 );\n l = _Math.clamp( l, 0, 1 );\n\n if ( s === 0 ) {\n\n this.r = this.g = this.b = l;\n\n } else {\n\n var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );\n var q = ( 2 * l ) - p;\n\n this.r = hue2rgb( q, p, h + 1 / 3 );\n this.g = hue2rgb( q, p, h );\n this.b = hue2rgb( q, p, h - 1 / 3 );\n\n }\n\n return this;\n\n };\n\n }(),\n\n setStyle: function ( style ) {\n\n function handleAlpha( string ) {\n\n if ( string === undefined ) return;\n\n if ( parseFloat( string ) < 1 ) {\n\n console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );\n\n }\n\n }\n\n\n var m;\n\n if ( m = /^((?:rgb|hsl)a?)\\(\\s*([^\\)]*)\\)/.exec( style ) ) {\n\n // rgb / hsl\n\n var color;\n var name = m[ 1 ];\n var components = m[ 2 ];\n\n switch ( name ) {\n\n case 'rgb':\n case 'rgba':\n\n if ( color = /^(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // rgb(255,0,0) rgba(255,0,0,0.5)\n this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;\n this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;\n this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;\n\n handleAlpha( color[ 5 ] );\n\n return this;\n\n }\n\n if ( color = /^(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)\n this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;\n this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;\n this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;\n\n handleAlpha( color[ 5 ] );\n\n return this;\n\n }\n\n break;\n\n case 'hsl':\n case 'hsla':\n\n if ( color = /^([0-9]*\\.?[0-9]+)\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // hsl(120,50%,50%) hsla(120,50%,50%,0.5)\n var h = parseFloat( color[ 1 ] ) / 360;\n var s = parseInt( color[ 2 ], 10 ) / 100;\n var l = parseInt( color[ 3 ], 10 ) / 100;\n\n handleAlpha( color[ 5 ] );\n\n return this.setHSL( h, s, l );\n\n }\n\n break;\n\n }\n\n } else if ( m = /^\\#([A-Fa-f0-9]+)$/.exec( style ) ) {\n\n // hex color\n\n var hex = m[ 1 ];\n var size = hex.length;\n\n if ( size === 3 ) {\n\n // #ff0\n this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;\n this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;\n this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;\n\n return this;\n\n } else if ( size === 6 ) {\n\n // #ff0000\n this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;\n this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;\n this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;\n\n return this;\n\n }\n\n }\n\n if ( style && style.length > 0 ) {\n\n // color keywords\n var hex = ColorKeywords[ style ];\n\n if ( hex !== undefined ) {\n\n // red\n this.setHex( hex );\n\n } else {\n\n // unknown color\n console.warn( 'THREE.Color: Unknown color ' + style );\n\n }\n\n }\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this.r, this.g, this.b );\n\n },\n\n copy: function ( color ) {\n\n this.r = color.r;\n this.g = color.g;\n this.b = color.b;\n\n return this;\n\n },\n\n copyGammaToLinear: function ( color, gammaFactor ) {\n\n if ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n this.r = Math.pow( color.r, gammaFactor );\n this.g = Math.pow( color.g, gammaFactor );\n this.b = Math.pow( color.b, gammaFactor );\n\n return this;\n\n },\n\n copyLinearToGamma: function ( color, gammaFactor ) {\n\n if ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;\n\n this.r = Math.pow( color.r, safeInverse );\n this.g = Math.pow( color.g, safeInverse );\n this.b = Math.pow( color.b, safeInverse );\n\n return this;\n\n },\n\n convertGammaToLinear: function () {\n\n var r = this.r, g = this.g, b = this.b;\n\n this.r = r * r;\n this.g = g * g;\n this.b = b * b;\n\n return this;\n\n },\n\n convertLinearToGamma: function () {\n\n this.r = Math.sqrt( this.r );\n this.g = Math.sqrt( this.g );\n this.b = Math.sqrt( this.b );\n\n return this;\n\n },\n\n getHex: function () {\n\n return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;\n\n },\n\n getHexString: function () {\n\n return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );\n\n },\n\n getHSL: function ( target ) {\n\n // h,s,l ranges are in 0.0 - 1.0\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Color: .getHSL() target is now required' );\n target = { h: 0, s: 0, l: 0 };\n\n }\n\n var r = this.r, g = this.g, b = this.b;\n\n var max = Math.max( r, g, b );\n var min = Math.min( r, g, b );\n\n var hue, saturation;\n var lightness = ( min + max ) / 2.0;\n\n if ( min === max ) {\n\n hue = 0;\n saturation = 0;\n\n } else {\n\n var delta = max - min;\n\n saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );\n\n switch ( max ) {\n\n case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;\n case g: hue = ( b - r ) / delta + 2; break;\n case b: hue = ( r - g ) / delta + 4; break;\n\n }\n\n hue /= 6;\n\n }\n\n target.h = hue;\n target.s = saturation;\n target.l = lightness;\n\n return target;\n\n },\n\n getStyle: function () {\n\n return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';\n\n },\n\n offsetHSL: function () {\n\n var hsl = {};\n\n return function ( h, s, l ) {\n\n this.getHSL( hsl );\n\n hsl.h += h; hsl.s += s; hsl.l += l;\n\n this.setHSL( hsl.h, hsl.s, hsl.l );\n\n return this;\n\n };\n\n }(),\n\n add: function ( color ) {\n\n this.r += color.r;\n this.g += color.g;\n this.b += color.b;\n\n return this;\n\n },\n\n addColors: function ( color1, color2 ) {\n\n this.r = color1.r + color2.r;\n this.g = color1.g + color2.g;\n this.b = color1.b + color2.b;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.r += s;\n this.g += s;\n this.b += s;\n\n return this;\n\n },\n\n sub: function ( color ) {\n\n this.r = Math.max( 0, this.r - color.r );\n this.g = Math.max( 0, this.g - color.g );\n this.b = Math.max( 0, this.b - color.b );\n\n return this;\n\n },\n\n multiply: function ( color ) {\n\n this.r *= color.r;\n this.g *= color.g;\n this.b *= color.b;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n this.r *= s;\n this.g *= s;\n this.b *= s;\n\n return this;\n\n },\n\n lerp: function ( color, alpha ) {\n\n this.r += ( color.r - this.r ) * alpha;\n this.g += ( color.g - this.g ) * alpha;\n this.b += ( color.b - this.b ) * alpha;\n\n return this;\n\n },\n\n equals: function ( c ) {\n\n return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.r = array[ offset ];\n this.g = array[ offset + 1 ];\n this.b = array[ offset + 2 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.r;\n array[ offset + 1 ] = this.g;\n array[ offset + 2 ] = this.b;\n\n return array;\n\n },\n\n toJSON: function () {\n\n return this.getHex();\n\n }\n\n } );\n\n /**\n * Uniforms library for shared webgl shaders\n */\n\n var UniformsLib = {\n\n common: {\n\n diffuse: { value: new Color( 0xeeeeee ) },\n opacity: { value: 1.0 },\n\n map: { value: null },\n uvTransform: { value: new Matrix3() },\n\n alphaMap: { value: null },\n\n },\n\n specularmap: {\n\n specularMap: { value: null },\n\n },\n\n envmap: {\n\n envMap: { value: null },\n flipEnvMap: { value: - 1 },\n reflectivity: { value: 1.0 },\n refractionRatio: { value: 0.98 },\n maxMipLevel: { value: 0 }\n\n },\n\n aomap: {\n\n aoMap: { value: null },\n aoMapIntensity: { value: 1 }\n\n },\n\n lightmap: {\n\n lightMap: { value: null },\n lightMapIntensity: { value: 1 }\n\n },\n\n emissivemap: {\n\n emissiveMap: { value: null }\n\n },\n\n bumpmap: {\n\n bumpMap: { value: null },\n bumpScale: { value: 1 }\n\n },\n\n normalmap: {\n\n normalMap: { value: null },\n normalScale: { value: new Vector2( 1, 1 ) }\n\n },\n\n displacementmap: {\n\n displacementMap: { value: null },\n displacementScale: { value: 1 },\n displacementBias: { value: 0 }\n\n },\n\n roughnessmap: {\n\n roughnessMap: { value: null }\n\n },\n\n metalnessmap: {\n\n metalnessMap: { value: null }\n\n },\n\n gradientmap: {\n\n gradientMap: { value: null }\n\n },\n\n fog: {\n\n fogDensity: { value: 0.00025 },\n fogNear: { value: 1 },\n fogFar: { value: 2000 },\n fogColor: { value: new Color( 0xffffff ) }\n\n },\n\n lights: {\n\n ambientLightColor: { value: [] },\n\n directionalLights: { value: [], properties: {\n direction: {},\n color: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {}\n } },\n\n directionalShadowMap: { value: [] },\n directionalShadowMatrix: { value: [] },\n\n spotLights: { value: [], properties: {\n color: {},\n position: {},\n direction: {},\n distance: {},\n coneCos: {},\n penumbraCos: {},\n decay: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {}\n } },\n\n spotShadowMap: { value: [] },\n spotShadowMatrix: { value: [] },\n\n pointLights: { value: [], properties: {\n color: {},\n position: {},\n decay: {},\n distance: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {},\n shadowCameraNear: {},\n shadowCameraFar: {}\n } },\n\n pointShadowMap: { value: [] },\n pointShadowMatrix: { value: [] },\n\n hemisphereLights: { value: [], properties: {\n direction: {},\n skyColor: {},\n groundColor: {}\n } },\n\n // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src\n rectAreaLights: { value: [], properties: {\n color: {},\n position: {},\n width: {},\n height: {}\n } }\n\n },\n\n points: {\n\n diffuse: { value: new Color( 0xeeeeee ) },\n opacity: { value: 1.0 },\n size: { value: 1.0 },\n scale: { value: 1.0 },\n map: { value: null },\n uvTransform: { value: new Matrix3() }\n\n }\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n */\n\n var ShaderLib = {\n\n basic: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.fog\n ] ),\n\n vertexShader: ShaderChunk.meshbasic_vert,\n fragmentShader: ShaderChunk.meshbasic_frag\n\n },\n\n lambert: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshlambert_vert,\n fragmentShader: ShaderChunk.meshlambert_frag\n\n },\n\n phong: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n UniformsLib.gradientmap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) },\n specular: { value: new Color( 0x111111 ) },\n shininess: { value: 30 }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphong_vert,\n fragmentShader: ShaderChunk.meshphong_frag\n\n },\n\n standard: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n UniformsLib.roughnessmap,\n UniformsLib.metalnessmap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) },\n roughness: { value: 0.5 },\n metalness: { value: 0.5 },\n envMapIntensity: { value: 1 } // temporary\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphysical_vert,\n fragmentShader: ShaderChunk.meshphysical_frag\n\n },\n\n points: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.points,\n UniformsLib.fog\n ] ),\n\n vertexShader: ShaderChunk.points_vert,\n fragmentShader: ShaderChunk.points_frag\n\n },\n\n dashed: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.fog,\n {\n scale: { value: 1 },\n dashSize: { value: 1 },\n totalSize: { value: 2 }\n }\n ] ),\n\n vertexShader: ShaderChunk.linedashed_vert,\n fragmentShader: ShaderChunk.linedashed_frag\n\n },\n\n depth: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.displacementmap\n ] ),\n\n vertexShader: ShaderChunk.depth_vert,\n fragmentShader: ShaderChunk.depth_frag\n\n },\n\n normal: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n {\n opacity: { value: 1.0 }\n }\n ] ),\n\n vertexShader: ShaderChunk.normal_vert,\n fragmentShader: ShaderChunk.normal_frag\n\n },\n\n /* -------------------------------------------------------------------------\n // Cube map shader\n ------------------------------------------------------------------------- */\n\n cube: {\n\n uniforms: {\n tCube: { value: null },\n tFlip: { value: - 1 },\n opacity: { value: 1.0 }\n },\n\n vertexShader: ShaderChunk.cube_vert,\n fragmentShader: ShaderChunk.cube_frag\n\n },\n\n equirect: {\n\n uniforms: {\n tEquirect: { value: null },\n },\n\n vertexShader: ShaderChunk.equirect_vert,\n fragmentShader: ShaderChunk.equirect_frag\n\n },\n\n distanceRGBA: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.displacementmap,\n {\n referencePosition: { value: new Vector3() },\n nearDistance: { value: 1 },\n farDistance: { value: 1000 }\n }\n ] ),\n\n vertexShader: ShaderChunk.distanceRGBA_vert,\n fragmentShader: ShaderChunk.distanceRGBA_frag\n\n },\n\n shadow: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.lights,\n UniformsLib.fog,\n {\n color: { value: new Color( 0x00000 ) },\n opacity: { value: 1.0 }\n },\n ] ),\n\n vertexShader: ShaderChunk.shadow_vert,\n fragmentShader: ShaderChunk.shadow_frag\n\n }\n\n };\n\n ShaderLib.physical = {\n\n uniforms: UniformsUtils.merge( [\n ShaderLib.standard.uniforms,\n {\n clearCoat: { value: 0 },\n clearCoatRoughness: { value: 0 }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphysical_vert,\n fragmentShader: ShaderChunk.meshphysical_frag\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLAttributes( gl ) {\n\n var buffers = new WeakMap();\n\n function createBuffer( attribute, bufferType ) {\n\n var array = attribute.array;\n var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;\n\n var buffer = gl.createBuffer();\n\n gl.bindBuffer( bufferType, buffer );\n gl.bufferData( bufferType, array, usage );\n\n attribute.onUploadCallback();\n\n var type = gl.FLOAT;\n\n if ( array instanceof Float32Array ) {\n\n type = gl.FLOAT;\n\n } else if ( array instanceof Float64Array ) {\n\n console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );\n\n } else if ( array instanceof Uint16Array ) {\n\n type = gl.UNSIGNED_SHORT;\n\n } else if ( array instanceof Int16Array ) {\n\n type = gl.SHORT;\n\n } else if ( array instanceof Uint32Array ) {\n\n type = gl.UNSIGNED_INT;\n\n } else if ( array instanceof Int32Array ) {\n\n type = gl.INT;\n\n } else if ( array instanceof Int8Array ) {\n\n type = gl.BYTE;\n\n } else if ( array instanceof Uint8Array ) {\n\n type = gl.UNSIGNED_BYTE;\n\n }\n\n return {\n buffer: buffer,\n type: type,\n bytesPerElement: array.BYTES_PER_ELEMENT,\n version: attribute.version\n };\n\n }\n\n function updateBuffer( buffer, attribute, bufferType ) {\n\n var array = attribute.array;\n var updateRange = attribute.updateRange;\n\n gl.bindBuffer( bufferType, buffer );\n\n if ( attribute.dynamic === false ) {\n\n gl.bufferData( bufferType, array, gl.STATIC_DRAW );\n\n } else if ( updateRange.count === - 1 ) {\n\n // Not using update ranges\n\n gl.bufferSubData( bufferType, 0, array );\n\n } else if ( updateRange.count === 0 ) {\n\n console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );\n\n } else {\n\n gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );\n\n updateRange.count = - 1; // reset range\n\n }\n\n }\n\n //\n\n function get( attribute ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n return buffers.get( attribute );\n\n }\n\n function remove( attribute ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n var data = buffers.get( attribute );\n\n if ( data ) {\n\n gl.deleteBuffer( data.buffer );\n\n buffers.delete( attribute );\n\n }\n\n }\n\n function update( attribute, bufferType ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n var data = buffers.get( attribute );\n\n if ( data === undefined ) {\n\n buffers.set( attribute, createBuffer( attribute, bufferType ) );\n\n } else if ( data.version < attribute.version ) {\n\n updateBuffer( data.buffer, attribute, bufferType );\n\n data.version = attribute.version;\n\n }\n\n }\n\n return {\n\n get: get,\n remove: remove,\n update: update\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n */\n\n function Euler( x, y, z, order ) {\n\n this._x = x || 0;\n this._y = y || 0;\n this._z = z || 0;\n this._order = order || Euler.DefaultOrder;\n\n }\n\n Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];\n\n Euler.DefaultOrder = 'XYZ';\n\n Object.defineProperties( Euler.prototype, {\n\n x: {\n\n get: function () {\n\n return this._x;\n\n },\n\n set: function ( value ) {\n\n this._x = value;\n this.onChangeCallback();\n\n }\n\n },\n\n y: {\n\n get: function () {\n\n return this._y;\n\n },\n\n set: function ( value ) {\n\n this._y = value;\n this.onChangeCallback();\n\n }\n\n },\n\n z: {\n\n get: function () {\n\n return this._z;\n\n },\n\n set: function ( value ) {\n\n this._z = value;\n this.onChangeCallback();\n\n }\n\n },\n\n order: {\n\n get: function () {\n\n return this._order;\n\n },\n\n set: function ( value ) {\n\n this._order = value;\n this.onChangeCallback();\n\n }\n\n }\n\n } );\n\n Object.assign( Euler.prototype, {\n\n isEuler: true,\n\n set: function ( x, y, z, order ) {\n\n this._x = x;\n this._y = y;\n this._z = z;\n this._order = order || this._order;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this._x, this._y, this._z, this._order );\n\n },\n\n copy: function ( euler ) {\n\n this._x = euler._x;\n this._y = euler._y;\n this._z = euler._z;\n this._order = euler._order;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromRotationMatrix: function ( m, order, update ) {\n\n var clamp = _Math.clamp;\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var te = m.elements;\n var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];\n var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];\n var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n order = order || this._order;\n\n if ( order === 'XYZ' ) {\n\n this._y = Math.asin( clamp( m13, - 1, 1 ) );\n\n if ( Math.abs( m13 ) < 0.99999 ) {\n\n this._x = Math.atan2( - m23, m33 );\n this._z = Math.atan2( - m12, m11 );\n\n } else {\n\n this._x = Math.atan2( m32, m22 );\n this._z = 0;\n\n }\n\n } else if ( order === 'YXZ' ) {\n\n this._x = Math.asin( - clamp( m23, - 1, 1 ) );\n\n if ( Math.abs( m23 ) < 0.99999 ) {\n\n this._y = Math.atan2( m13, m33 );\n this._z = Math.atan2( m21, m22 );\n\n } else {\n\n this._y = Math.atan2( - m31, m11 );\n this._z = 0;\n\n }\n\n } else if ( order === 'ZXY' ) {\n\n this._x = Math.asin( clamp( m32, - 1, 1 ) );\n\n if ( Math.abs( m32 ) < 0.99999 ) {\n\n this._y = Math.atan2( - m31, m33 );\n this._z = Math.atan2( - m12, m22 );\n\n } else {\n\n this._y = 0;\n this._z = Math.atan2( m21, m11 );\n\n }\n\n } else if ( order === 'ZYX' ) {\n\n this._y = Math.asin( - clamp( m31, - 1, 1 ) );\n\n if ( Math.abs( m31 ) < 0.99999 ) {\n\n this._x = Math.atan2( m32, m33 );\n this._z = Math.atan2( m21, m11 );\n\n } else {\n\n this._x = 0;\n this._z = Math.atan2( - m12, m22 );\n\n }\n\n } else if ( order === 'YZX' ) {\n\n this._z = Math.asin( clamp( m21, - 1, 1 ) );\n\n if ( Math.abs( m21 ) < 0.99999 ) {\n\n this._x = Math.atan2( - m23, m22 );\n this._y = Math.atan2( - m31, m11 );\n\n } else {\n\n this._x = 0;\n this._y = Math.atan2( m13, m33 );\n\n }\n\n } else if ( order === 'XZY' ) {\n\n this._z = Math.asin( - clamp( m12, - 1, 1 ) );\n\n if ( Math.abs( m12 ) < 0.99999 ) {\n\n this._x = Math.atan2( m32, m22 );\n this._y = Math.atan2( m13, m11 );\n\n } else {\n\n this._x = Math.atan2( - m23, m33 );\n this._y = 0;\n\n }\n\n } else {\n\n console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order );\n\n }\n\n this._order = order;\n\n if ( update !== false ) this.onChangeCallback();\n\n return this;\n\n },\n\n setFromQuaternion: function () {\n\n var matrix = new Matrix4();\n\n return function setFromQuaternion( q, order, update ) {\n\n matrix.makeRotationFromQuaternion( q );\n\n return this.setFromRotationMatrix( matrix, order, update );\n\n };\n\n }(),\n\n setFromVector3: function ( v, order ) {\n\n return this.set( v.x, v.y, v.z, order || this._order );\n\n },\n\n reorder: function () {\n\n // WARNING: this discards revolution information -bhouston\n\n var q = new Quaternion();\n\n return function reorder( newOrder ) {\n\n q.setFromEuler( this );\n\n return this.setFromQuaternion( q, newOrder );\n\n };\n\n }(),\n\n equals: function ( euler ) {\n\n return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );\n\n },\n\n fromArray: function ( array ) {\n\n this._x = array[ 0 ];\n this._y = array[ 1 ];\n this._z = array[ 2 ];\n if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this._x;\n array[ offset + 1 ] = this._y;\n array[ offset + 2 ] = this._z;\n array[ offset + 3 ] = this._order;\n\n return array;\n\n },\n\n toVector3: function ( optionalResult ) {\n\n if ( optionalResult ) {\n\n return optionalResult.set( this._x, this._y, this._z );\n\n } else {\n\n return new Vector3( this._x, this._y, this._z );\n\n }\n\n },\n\n onChange: function ( callback ) {\n\n this.onChangeCallback = callback;\n\n return this;\n\n },\n\n onChangeCallback: function () {}\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Layers() {\n\n this.mask = 1 | 0;\n\n }\n\n Object.assign( Layers.prototype, {\n\n set: function ( channel ) {\n\n this.mask = 1 << channel | 0;\n\n },\n\n enable: function ( channel ) {\n\n this.mask |= 1 << channel | 0;\n\n },\n\n toggle: function ( channel ) {\n\n this.mask ^= 1 << channel | 0;\n\n },\n\n disable: function ( channel ) {\n\n this.mask &= ~ ( 1 << channel | 0 );\n\n },\n\n test: function ( layers ) {\n\n return ( this.mask & layers.mask ) !== 0;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author elephantatwork / www.elephantatwork.ch\n */\n\n var object3DId = 0;\n\n function Object3D() {\n\n Object.defineProperty( this, 'id', { value: object3DId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Object3D';\n\n this.parent = null;\n this.children = [];\n\n this.up = Object3D.DefaultUp.clone();\n\n var position = new Vector3();\n var rotation = new Euler();\n var quaternion = new Quaternion();\n var scale = new Vector3( 1, 1, 1 );\n\n function onRotationChange() {\n\n quaternion.setFromEuler( rotation, false );\n\n }\n\n function onQuaternionChange() {\n\n rotation.setFromQuaternion( quaternion, undefined, false );\n\n }\n\n rotation.onChange( onRotationChange );\n quaternion.onChange( onQuaternionChange );\n\n Object.defineProperties( this, {\n position: {\n enumerable: true,\n value: position\n },\n rotation: {\n enumerable: true,\n value: rotation\n },\n quaternion: {\n enumerable: true,\n value: quaternion\n },\n scale: {\n enumerable: true,\n value: scale\n },\n modelViewMatrix: {\n value: new Matrix4()\n },\n normalMatrix: {\n value: new Matrix3()\n }\n } );\n\n this.matrix = new Matrix4();\n this.matrixWorld = new Matrix4();\n\n this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;\n this.matrixWorldNeedsUpdate = false;\n\n this.layers = new Layers();\n this.visible = true;\n\n this.castShadow = false;\n this.receiveShadow = false;\n\n this.frustumCulled = true;\n this.renderOrder = 0;\n\n this.userData = {};\n\n }\n\n Object3D.DefaultUp = new Vector3( 0, 1, 0 );\n Object3D.DefaultMatrixAutoUpdate = true;\n\n Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Object3D,\n\n isObject3D: true,\n\n onBeforeRender: function () {},\n onAfterRender: function () {},\n\n applyMatrix: function ( matrix ) {\n\n this.matrix.multiplyMatrices( matrix, this.matrix );\n\n this.matrix.decompose( this.position, this.quaternion, this.scale );\n\n },\n\n applyQuaternion: function ( q ) {\n\n this.quaternion.premultiply( q );\n\n return this;\n\n },\n\n setRotationFromAxisAngle: function ( axis, angle ) {\n\n // assumes axis is normalized\n\n this.quaternion.setFromAxisAngle( axis, angle );\n\n },\n\n setRotationFromEuler: function ( euler ) {\n\n this.quaternion.setFromEuler( euler, true );\n\n },\n\n setRotationFromMatrix: function ( m ) {\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n this.quaternion.setFromRotationMatrix( m );\n\n },\n\n setRotationFromQuaternion: function ( q ) {\n\n // assumes q is normalized\n\n this.quaternion.copy( q );\n\n },\n\n rotateOnAxis: function () {\n\n // rotate object on axis in object space\n // axis is assumed to be normalized\n\n var q1 = new Quaternion();\n\n return function rotateOnAxis( axis, angle ) {\n\n q1.setFromAxisAngle( axis, angle );\n\n this.quaternion.multiply( q1 );\n\n return this;\n\n };\n\n }(),\n\n rotateOnWorldAxis: function () {\n\n // rotate object on axis in world space\n // axis is assumed to be normalized\n // method assumes no rotated parent\n\n var q1 = new Quaternion();\n\n return function rotateOnWorldAxis( axis, angle ) {\n\n q1.setFromAxisAngle( axis, angle );\n\n this.quaternion.premultiply( q1 );\n\n return this;\n\n };\n\n }(),\n\n rotateX: function () {\n\n var v1 = new Vector3( 1, 0, 0 );\n\n return function rotateX( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n rotateY: function () {\n\n var v1 = new Vector3( 0, 1, 0 );\n\n return function rotateY( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n rotateZ: function () {\n\n var v1 = new Vector3( 0, 0, 1 );\n\n return function rotateZ( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n translateOnAxis: function () {\n\n // translate object by distance along axis in object space\n // axis is assumed to be normalized\n\n var v1 = new Vector3();\n\n return function translateOnAxis( axis, distance ) {\n\n v1.copy( axis ).applyQuaternion( this.quaternion );\n\n this.position.add( v1.multiplyScalar( distance ) );\n\n return this;\n\n };\n\n }(),\n\n translateX: function () {\n\n var v1 = new Vector3( 1, 0, 0 );\n\n return function translateX( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n translateY: function () {\n\n var v1 = new Vector3( 0, 1, 0 );\n\n return function translateY( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n translateZ: function () {\n\n var v1 = new Vector3( 0, 0, 1 );\n\n return function translateZ( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n localToWorld: function ( vector ) {\n\n return vector.applyMatrix4( this.matrixWorld );\n\n },\n\n worldToLocal: function () {\n\n var m1 = new Matrix4();\n\n return function worldToLocal( vector ) {\n\n return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );\n\n };\n\n }(),\n\n lookAt: function () {\n\n // This method does not support objects with rotated and/or translated parent(s)\n\n var m1 = new Matrix4();\n var vector = new Vector3();\n\n return function lookAt( x, y, z ) {\n\n if ( x.isVector3 ) {\n\n vector.copy( x );\n\n } else {\n\n vector.set( x, y, z );\n\n }\n\n if ( this.isCamera ) {\n\n m1.lookAt( this.position, vector, this.up );\n\n } else {\n\n m1.lookAt( vector, this.position, this.up );\n\n }\n\n this.quaternion.setFromRotationMatrix( m1 );\n\n };\n\n }(),\n\n add: function ( object ) {\n\n if ( arguments.length > 1 ) {\n\n for ( var i = 0; i < arguments.length; i ++ ) {\n\n this.add( arguments[ i ] );\n\n }\n\n return this;\n\n }\n\n if ( object === this ) {\n\n console.error( \"THREE.Object3D.add: object can't be added as a child of itself.\", object );\n return this;\n\n }\n\n if ( ( object && object.isObject3D ) ) {\n\n if ( object.parent !== null ) {\n\n object.parent.remove( object );\n\n }\n\n object.parent = this;\n object.dispatchEvent( { type: 'added' } );\n\n this.children.push( object );\n\n } else {\n\n console.error( \"THREE.Object3D.add: object not an instance of THREE.Object3D.\", object );\n\n }\n\n return this;\n\n },\n\n remove: function ( object ) {\n\n if ( arguments.length > 1 ) {\n\n for ( var i = 0; i < arguments.length; i ++ ) {\n\n this.remove( arguments[ i ] );\n\n }\n\n return this;\n\n }\n\n var index = this.children.indexOf( object );\n\n if ( index !== - 1 ) {\n\n object.parent = null;\n\n object.dispatchEvent( { type: 'removed' } );\n\n this.children.splice( index, 1 );\n\n }\n\n return this;\n\n },\n\n getObjectById: function ( id ) {\n\n return this.getObjectByProperty( 'id', id );\n\n },\n\n getObjectByName: function ( name ) {\n\n return this.getObjectByProperty( 'name', name );\n\n },\n\n getObjectByProperty: function ( name, value ) {\n\n if ( this[ name ] === value ) return this;\n\n for ( var i = 0, l = this.children.length; i < l; i ++ ) {\n\n var child = this.children[ i ];\n var object = child.getObjectByProperty( name, value );\n\n if ( object !== undefined ) {\n\n return object;\n\n }\n\n }\n\n return undefined;\n\n },\n\n getWorldPosition: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );\n target = new Vector3();\n\n }\n\n this.updateMatrixWorld( true );\n\n return target.setFromMatrixPosition( this.matrixWorld );\n\n },\n\n getWorldQuaternion: function () {\n\n var position = new Vector3();\n var scale = new Vector3();\n\n return function getWorldQuaternion( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );\n target = new Quaternion();\n\n }\n\n this.updateMatrixWorld( true );\n\n this.matrixWorld.decompose( position, target, scale );\n\n return target;\n\n };\n\n }(),\n\n getWorldScale: function () {\n\n var position = new Vector3();\n var quaternion = new Quaternion();\n\n return function getWorldScale( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );\n target = new Vector3();\n\n }\n\n this.updateMatrixWorld( true );\n\n this.matrixWorld.decompose( position, quaternion, target );\n\n return target;\n\n };\n\n }(),\n\n getWorldDirection: function () {\n\n var quaternion = new Quaternion();\n\n return function getWorldDirection( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );\n target = new Vector3();\n\n }\n\n this.getWorldQuaternion( quaternion );\n\n return target.set( 0, 0, 1 ).applyQuaternion( quaternion );\n\n };\n\n }(),\n\n raycast: function () {},\n\n traverse: function ( callback ) {\n\n callback( this );\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].traverse( callback );\n\n }\n\n },\n\n traverseVisible: function ( callback ) {\n\n if ( this.visible === false ) return;\n\n callback( this );\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].traverseVisible( callback );\n\n }\n\n },\n\n traverseAncestors: function ( callback ) {\n\n var parent = this.parent;\n\n if ( parent !== null ) {\n\n callback( parent );\n\n parent.traverseAncestors( callback );\n\n }\n\n },\n\n updateMatrix: function () {\n\n this.matrix.compose( this.position, this.quaternion, this.scale );\n\n this.matrixWorldNeedsUpdate = true;\n\n },\n\n updateMatrixWorld: function ( force ) {\n\n if ( this.matrixAutoUpdate ) this.updateMatrix();\n\n if ( this.matrixWorldNeedsUpdate || force ) {\n\n if ( this.parent === null ) {\n\n this.matrixWorld.copy( this.matrix );\n\n } else {\n\n this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n }\n\n this.matrixWorldNeedsUpdate = false;\n\n force = true;\n\n }\n\n // update children\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].updateMatrixWorld( force );\n\n }\n\n },\n\n toJSON: function ( meta ) {\n\n // meta is a string when called from JSON.stringify\n var isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n var output = {};\n\n // meta is a hash used to collect geometries, materials.\n // not providing it implies that this is the root object\n // being serialized.\n if ( isRootObject ) {\n\n // initialize meta obj\n meta = {\n geometries: {},\n materials: {},\n textures: {},\n images: {},\n shapes: {}\n };\n\n output.metadata = {\n version: 4.5,\n type: 'Object',\n generator: 'Object3D.toJSON'\n };\n\n }\n\n // standard Object3D serialization\n\n var object = {};\n\n object.uuid = this.uuid;\n object.type = this.type;\n\n if ( this.name !== '' ) object.name = this.name;\n if ( this.castShadow === true ) object.castShadow = true;\n if ( this.receiveShadow === true ) object.receiveShadow = true;\n if ( this.visible === false ) object.visible = false;\n if ( this.frustumCulled === false ) object.frustumCulled = false;\n if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;\n if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;\n\n object.matrix = this.matrix.toArray();\n\n //\n\n function serialize( library, element ) {\n\n if ( library[ element.uuid ] === undefined ) {\n\n library[ element.uuid ] = element.toJSON( meta );\n\n }\n\n return element.uuid;\n\n }\n\n if ( this.geometry !== undefined ) {\n\n object.geometry = serialize( meta.geometries, this.geometry );\n\n var parameters = this.geometry.parameters;\n\n if ( parameters !== undefined && parameters.shapes !== undefined ) {\n\n var shapes = parameters.shapes;\n\n if ( Array.isArray( shapes ) ) {\n\n for ( var i = 0, l = shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ i ];\n\n serialize( meta.shapes, shape );\n\n }\n\n } else {\n\n serialize( meta.shapes, shapes );\n\n }\n\n }\n\n }\n\n if ( this.material !== undefined ) {\n\n if ( Array.isArray( this.material ) ) {\n\n var uuids = [];\n\n for ( var i = 0, l = this.material.length; i < l; i ++ ) {\n\n uuids.push( serialize( meta.materials, this.material[ i ] ) );\n\n }\n\n object.material = uuids;\n\n } else {\n\n object.material = serialize( meta.materials, this.material );\n\n }\n\n }\n\n //\n\n if ( this.children.length > 0 ) {\n\n object.children = [];\n\n for ( var i = 0; i < this.children.length; i ++ ) {\n\n object.children.push( this.children[ i ].toJSON( meta ).object );\n\n }\n\n }\n\n if ( isRootObject ) {\n\n var geometries = extractFromCache( meta.geometries );\n var materials = extractFromCache( meta.materials );\n var textures = extractFromCache( meta.textures );\n var images = extractFromCache( meta.images );\n var shapes = extractFromCache( meta.shapes );\n\n if ( geometries.length > 0 ) output.geometries = geometries;\n if ( materials.length > 0 ) output.materials = materials;\n if ( textures.length > 0 ) output.textures = textures;\n if ( images.length > 0 ) output.images = images;\n if ( shapes.length > 0 ) output.shapes = shapes;\n\n }\n\n output.object = object;\n\n return output;\n\n // extract data from the cache hash\n // remove metadata on each item\n // and return as array\n function extractFromCache( cache ) {\n\n var values = [];\n for ( var key in cache ) {\n\n var data = cache[ key ];\n delete data.metadata;\n values.push( data );\n\n }\n return values;\n\n }\n\n },\n\n clone: function ( recursive ) {\n\n return new this.constructor().copy( this, recursive );\n\n },\n\n copy: function ( source, recursive ) {\n\n if ( recursive === undefined ) recursive = true;\n\n this.name = source.name;\n\n this.up.copy( source.up );\n\n this.position.copy( source.position );\n this.quaternion.copy( source.quaternion );\n this.scale.copy( source.scale );\n\n this.matrix.copy( source.matrix );\n this.matrixWorld.copy( source.matrixWorld );\n\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;\n\n this.layers.mask = source.layers.mask;\n this.visible = source.visible;\n\n this.castShadow = source.castShadow;\n this.receiveShadow = source.receiveShadow;\n\n this.frustumCulled = source.frustumCulled;\n this.renderOrder = source.renderOrder;\n\n this.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n if ( recursive === true ) {\n\n for ( var i = 0; i < source.children.length; i ++ ) {\n\n var child = source.children[ i ];\n this.add( child.clone() );\n\n }\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Camera() {\n\n Object3D.call( this );\n\n this.type = 'Camera';\n\n this.matrixWorldInverse = new Matrix4();\n this.projectionMatrix = new Matrix4();\n\n }\n\n Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Camera,\n\n isCamera: true,\n\n copy: function ( source, recursive ) {\n\n Object3D.prototype.copy.call( this, source, recursive );\n\n this.matrixWorldInverse.copy( source.matrixWorldInverse );\n this.projectionMatrix.copy( source.projectionMatrix );\n\n return this;\n\n },\n\n getWorldDirection: function () {\n\n var quaternion = new Quaternion();\n\n return function getWorldDirection( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Camera: .getWorldDirection() target is now required' );\n target = new Vector3();\n\n }\n\n this.getWorldQuaternion( quaternion );\n\n return target.set( 0, 0, - 1 ).applyQuaternion( quaternion );\n\n };\n\n }(),\n\n updateMatrixWorld: function ( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n this.matrixWorldInverse.getInverse( this.matrixWorld );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author arose / http://github.com/arose\n */\n\n function OrthographicCamera( left, right, top, bottom, near, far ) {\n\n Camera.call( this );\n\n this.type = 'OrthographicCamera';\n\n this.zoom = 1;\n this.view = null;\n\n this.left = left;\n this.right = right;\n this.top = top;\n this.bottom = bottom;\n\n this.near = ( near !== undefined ) ? near : 0.1;\n this.far = ( far !== undefined ) ? far : 2000;\n\n this.updateProjectionMatrix();\n\n }\n\n OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n constructor: OrthographicCamera,\n\n isOrthographicCamera: true,\n\n copy: function ( source, recursive ) {\n\n Camera.prototype.copy.call( this, source, recursive );\n\n this.left = source.left;\n this.right = source.right;\n this.top = source.top;\n this.bottom = source.bottom;\n this.near = source.near;\n this.far = source.far;\n\n this.zoom = source.zoom;\n this.view = source.view === null ? null : Object.assign( {}, source.view );\n\n return this;\n\n },\n\n setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {\n\n if ( this.view === null ) {\n\n this.view = {\n enabled: true,\n fullWidth: 1,\n fullHeight: 1,\n offsetX: 0,\n offsetY: 0,\n width: 1,\n height: 1\n };\n\n }\n\n this.view.enabled = true;\n this.view.fullWidth = fullWidth;\n this.view.fullHeight = fullHeight;\n this.view.offsetX = x;\n this.view.offsetY = y;\n this.view.width = width;\n this.view.height = height;\n\n this.updateProjectionMatrix();\n\n },\n\n clearViewOffset: function () {\n\n if ( this.view !== null ) {\n\n this.view.enabled = false;\n\n }\n\n this.updateProjectionMatrix();\n\n },\n\n updateProjectionMatrix: function () {\n\n var dx = ( this.right - this.left ) / ( 2 * this.zoom );\n var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );\n var cx = ( this.right + this.left ) / 2;\n var cy = ( this.top + this.bottom ) / 2;\n\n var left = cx - dx;\n var right = cx + dx;\n var top = cy + dy;\n var bottom = cy - dy;\n\n if ( this.view !== null && this.view.enabled ) {\n\n var zoomW = this.zoom / ( this.view.width / this.view.fullWidth );\n var zoomH = this.zoom / ( this.view.height / this.view.fullHeight );\n var scaleW = ( this.right - this.left ) / this.view.width;\n var scaleH = ( this.top - this.bottom ) / this.view.height;\n\n left += scaleW * ( this.view.offsetX / zoomW );\n right = left + scaleW * ( this.view.width / zoomW );\n top -= scaleH * ( this.view.offsetY / zoomH );\n bottom = top - scaleH * ( this.view.height / zoomH );\n\n }\n\n this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far );\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.zoom = this.zoom;\n data.object.left = this.left;\n data.object.right = this.right;\n data.object.top = this.top;\n data.object.bottom = this.bottom;\n data.object.near = this.near;\n data.object.far = this.far;\n\n if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Face3( a, b, c, normal, color, materialIndex ) {\n\n this.a = a;\n this.b = b;\n this.c = c;\n\n this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3();\n this.vertexNormals = Array.isArray( normal ) ? normal : [];\n\n this.color = ( color && color.isColor ) ? color : new Color();\n this.vertexColors = Array.isArray( color ) ? color : [];\n\n this.materialIndex = materialIndex !== undefined ? materialIndex : 0;\n\n }\n\n Object.assign( Face3.prototype, {\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.a = source.a;\n this.b = source.b;\n this.c = source.c;\n\n this.normal.copy( source.normal );\n this.color.copy( source.color );\n\n this.materialIndex = source.materialIndex;\n\n for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {\n\n this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();\n\n }\n\n for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {\n\n this.vertexColors[ i ] = source.vertexColors[ i ].clone();\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author kile / http://kile.stravaganza.org/\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author bhouston / http://clara.io\n */\n\n var geometryId = 0; // Geometry uses even numbers as Id\n\n function Geometry() {\n\n Object.defineProperty( this, 'id', { value: geometryId += 2 } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Geometry';\n\n this.vertices = [];\n this.colors = [];\n this.faces = [];\n this.faceVertexUvs = [[]];\n\n this.morphTargets = [];\n this.morphNormals = [];\n\n this.skinWeights = [];\n this.skinIndices = [];\n\n this.lineDistances = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // update flags\n\n this.elementsNeedUpdate = false;\n this.verticesNeedUpdate = false;\n this.uvsNeedUpdate = false;\n this.normalsNeedUpdate = false;\n this.colorsNeedUpdate = false;\n this.lineDistancesNeedUpdate = false;\n this.groupsNeedUpdate = false;\n\n }\n\n Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Geometry,\n\n isGeometry: true,\n\n applyMatrix: function ( matrix ) {\n\n var normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n var vertex = this.vertices[ i ];\n vertex.applyMatrix4( matrix );\n\n }\n\n for ( var i = 0, il = this.faces.length; i < il; i ++ ) {\n\n var face = this.faces[ i ];\n face.normal.applyMatrix3( normalMatrix ).normalize();\n\n for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {\n\n face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();\n\n }\n\n }\n\n if ( this.boundingBox !== null ) {\n\n this.computeBoundingBox();\n\n }\n\n if ( this.boundingSphere !== null ) {\n\n this.computeBoundingSphere();\n\n }\n\n this.verticesNeedUpdate = true;\n this.normalsNeedUpdate = true;\n\n return this;\n\n },\n\n rotateX: function () {\n\n // rotate geometry around world x-axis\n\n var m1 = new Matrix4();\n\n return function rotateX( angle ) {\n\n m1.makeRotationX( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateY: function () {\n\n // rotate geometry around world y-axis\n\n var m1 = new Matrix4();\n\n return function rotateY( angle ) {\n\n m1.makeRotationY( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateZ: function () {\n\n // rotate geometry around world z-axis\n\n var m1 = new Matrix4();\n\n return function rotateZ( angle ) {\n\n m1.makeRotationZ( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n translate: function () {\n\n // translate geometry\n\n var m1 = new Matrix4();\n\n return function translate( x, y, z ) {\n\n m1.makeTranslation( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n scale: function () {\n\n // scale geometry\n\n var m1 = new Matrix4();\n\n return function scale( x, y, z ) {\n\n m1.makeScale( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n lookAt: function () {\n\n var obj = new Object3D();\n\n return function lookAt( vector ) {\n\n obj.lookAt( vector );\n\n obj.updateMatrix();\n\n this.applyMatrix( obj.matrix );\n\n };\n\n }(),\n\n fromBufferGeometry: function ( geometry ) {\n\n var scope = this;\n\n var indices = geometry.index !== null ? geometry.index.array : undefined;\n var attributes = geometry.attributes;\n\n var positions = attributes.position.array;\n var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;\n var colors = attributes.color !== undefined ? attributes.color.array : undefined;\n var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;\n var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;\n\n if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];\n\n var tempNormals = [];\n var tempUVs = [];\n var tempUVs2 = [];\n\n for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {\n\n scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) );\n\n if ( normals !== undefined ) {\n\n tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );\n\n }\n\n if ( colors !== undefined ) {\n\n scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );\n\n }\n\n if ( uvs !== undefined ) {\n\n tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) );\n\n }\n\n if ( uvs2 !== undefined ) {\n\n tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );\n\n }\n\n }\n\n function addFace( a, b, c, materialIndex ) {\n\n var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];\n var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];\n\n var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );\n\n scope.faces.push( face );\n\n if ( uvs !== undefined ) {\n\n scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );\n\n }\n\n if ( uvs2 !== undefined ) {\n\n scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );\n\n }\n\n }\n\n var groups = geometry.groups;\n\n if ( groups.length > 0 ) {\n\n for ( var i = 0; i < groups.length; i ++ ) {\n\n var group = groups[ i ];\n\n var start = group.start;\n var count = group.count;\n\n for ( var j = start, jl = start + count; j < jl; j += 3 ) {\n\n if ( indices !== undefined ) {\n\n addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );\n\n } else {\n\n addFace( j, j + 1, j + 2, group.materialIndex );\n\n }\n\n }\n\n }\n\n } else {\n\n if ( indices !== undefined ) {\n\n for ( var i = 0; i < indices.length; i += 3 ) {\n\n addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );\n\n }\n\n } else {\n\n for ( var i = 0; i < positions.length / 3; i += 3 ) {\n\n addFace( i, i + 1, i + 2 );\n\n }\n\n }\n\n }\n\n this.computeFaceNormals();\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n return this;\n\n },\n\n center: function () {\n\n var offset = new Vector3();\n\n return function center() {\n\n this.computeBoundingBox();\n\n this.boundingBox.getCenter( offset ).negate();\n\n this.translate( offset.x, offset.y, offset.z );\n\n return this;\n\n };\n\n }(),\n\n normalize: function () {\n\n this.computeBoundingSphere();\n\n var center = this.boundingSphere.center;\n var radius = this.boundingSphere.radius;\n\n var s = radius === 0 ? 1 : 1.0 / radius;\n\n var matrix = new Matrix4();\n matrix.set(\n s, 0, 0, - s * center.x,\n 0, s, 0, - s * center.y,\n 0, 0, s, - s * center.z,\n 0, 0, 0, 1\n );\n\n this.applyMatrix( matrix );\n\n return this;\n\n },\n\n computeFaceNormals: function () {\n\n var cb = new Vector3(), ab = new Vector3();\n\n for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n var face = this.faces[ f ];\n\n var vA = this.vertices[ face.a ];\n var vB = this.vertices[ face.b ];\n var vC = this.vertices[ face.c ];\n\n cb.subVectors( vC, vB );\n ab.subVectors( vA, vB );\n cb.cross( ab );\n\n cb.normalize();\n\n face.normal.copy( cb );\n\n }\n\n },\n\n computeVertexNormals: function ( areaWeighted ) {\n\n if ( areaWeighted === undefined ) areaWeighted = true;\n\n var v, vl, f, fl, face, vertices;\n\n vertices = new Array( this.vertices.length );\n\n for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n vertices[ v ] = new Vector3();\n\n }\n\n if ( areaWeighted ) {\n\n // vertex normals weighted by triangle areas\n // http://www.iquilezles.org/www/articles/normals/normals.htm\n\n var vA, vB, vC;\n var cb = new Vector3(), ab = new Vector3();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n vA = this.vertices[ face.a ];\n vB = this.vertices[ face.b ];\n vC = this.vertices[ face.c ];\n\n cb.subVectors( vC, vB );\n ab.subVectors( vA, vB );\n cb.cross( ab );\n\n vertices[ face.a ].add( cb );\n vertices[ face.b ].add( cb );\n vertices[ face.c ].add( cb );\n\n }\n\n } else {\n\n this.computeFaceNormals();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n vertices[ face.a ].add( face.normal );\n vertices[ face.b ].add( face.normal );\n vertices[ face.c ].add( face.normal );\n\n }\n\n }\n\n for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n vertices[ v ].normalize();\n\n }\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n vertexNormals[ 0 ].copy( vertices[ face.a ] );\n vertexNormals[ 1 ].copy( vertices[ face.b ] );\n vertexNormals[ 2 ].copy( vertices[ face.c ] );\n\n } else {\n\n vertexNormals[ 0 ] = vertices[ face.a ].clone();\n vertexNormals[ 1 ] = vertices[ face.b ].clone();\n vertexNormals[ 2 ] = vertices[ face.c ].clone();\n\n }\n\n }\n\n if ( this.faces.length > 0 ) {\n\n this.normalsNeedUpdate = true;\n\n }\n\n },\n\n computeFlatVertexNormals: function () {\n\n var f, fl, face;\n\n this.computeFaceNormals();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n vertexNormals[ 0 ].copy( face.normal );\n vertexNormals[ 1 ].copy( face.normal );\n vertexNormals[ 2 ].copy( face.normal );\n\n } else {\n\n vertexNormals[ 0 ] = face.normal.clone();\n vertexNormals[ 1 ] = face.normal.clone();\n vertexNormals[ 2 ] = face.normal.clone();\n\n }\n\n }\n\n if ( this.faces.length > 0 ) {\n\n this.normalsNeedUpdate = true;\n\n }\n\n },\n\n computeMorphNormals: function () {\n\n var i, il, f, fl, face;\n\n // save original normals\n // - create temp variables on first access\n // otherwise just copy (for faster repeated calls)\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n if ( ! face.__originalFaceNormal ) {\n\n face.__originalFaceNormal = face.normal.clone();\n\n } else {\n\n face.__originalFaceNormal.copy( face.normal );\n\n }\n\n if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];\n\n for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {\n\n if ( ! face.__originalVertexNormals[ i ] ) {\n\n face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();\n\n } else {\n\n face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );\n\n }\n\n }\n\n }\n\n // use temp geometry to compute face and vertex normals for each morph\n\n var tmpGeo = new Geometry();\n tmpGeo.faces = this.faces;\n\n for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {\n\n // create on first access\n\n if ( ! this.morphNormals[ i ] ) {\n\n this.morphNormals[ i ] = {};\n this.morphNormals[ i ].faceNormals = [];\n this.morphNormals[ i ].vertexNormals = [];\n\n var dstNormalsFace = this.morphNormals[ i ].faceNormals;\n var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;\n\n var faceNormal, vertexNormals;\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n faceNormal = new Vector3();\n vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() };\n\n dstNormalsFace.push( faceNormal );\n dstNormalsVertex.push( vertexNormals );\n\n }\n\n }\n\n var morphNormals = this.morphNormals[ i ];\n\n // set vertices to morph target\n\n tmpGeo.vertices = this.morphTargets[ i ].vertices;\n\n // compute morph normals\n\n tmpGeo.computeFaceNormals();\n tmpGeo.computeVertexNormals();\n\n // store morph normals\n\n var faceNormal, vertexNormals;\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n faceNormal = morphNormals.faceNormals[ f ];\n vertexNormals = morphNormals.vertexNormals[ f ];\n\n faceNormal.copy( face.normal );\n\n vertexNormals.a.copy( face.vertexNormals[ 0 ] );\n vertexNormals.b.copy( face.vertexNormals[ 1 ] );\n vertexNormals.c.copy( face.vertexNormals[ 2 ] );\n\n }\n\n }\n\n // restore original normals\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n face.normal = face.__originalFaceNormal;\n face.vertexNormals = face.__originalVertexNormals;\n\n }\n\n },\n\n computeBoundingBox: function () {\n\n if ( this.boundingBox === null ) {\n\n this.boundingBox = new Box3();\n\n }\n\n this.boundingBox.setFromPoints( this.vertices );\n\n },\n\n computeBoundingSphere: function () {\n\n if ( this.boundingSphere === null ) {\n\n this.boundingSphere = new Sphere();\n\n }\n\n this.boundingSphere.setFromPoints( this.vertices );\n\n },\n\n merge: function ( geometry, matrix, materialIndexOffset ) {\n\n if ( ! ( geometry && geometry.isGeometry ) ) {\n\n console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );\n return;\n\n }\n\n var normalMatrix,\n vertexOffset = this.vertices.length,\n vertices1 = this.vertices,\n vertices2 = geometry.vertices,\n faces1 = this.faces,\n faces2 = geometry.faces,\n uvs1 = this.faceVertexUvs[ 0 ],\n uvs2 = geometry.faceVertexUvs[ 0 ],\n colors1 = this.colors,\n colors2 = geometry.colors;\n\n if ( materialIndexOffset === undefined ) materialIndexOffset = 0;\n\n if ( matrix !== undefined ) {\n\n normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n }\n\n // vertices\n\n for ( var i = 0, il = vertices2.length; i < il; i ++ ) {\n\n var vertex = vertices2[ i ];\n\n var vertexCopy = vertex.clone();\n\n if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );\n\n vertices1.push( vertexCopy );\n\n }\n\n // colors\n\n for ( var i = 0, il = colors2.length; i < il; i ++ ) {\n\n colors1.push( colors2[ i ].clone() );\n\n }\n\n // faces\n\n for ( i = 0, il = faces2.length; i < il; i ++ ) {\n\n var face = faces2[ i ], faceCopy, normal, color,\n faceVertexNormals = face.vertexNormals,\n faceVertexColors = face.vertexColors;\n\n faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );\n faceCopy.normal.copy( face.normal );\n\n if ( normalMatrix !== undefined ) {\n\n faceCopy.normal.applyMatrix3( normalMatrix ).normalize();\n\n }\n\n for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {\n\n normal = faceVertexNormals[ j ].clone();\n\n if ( normalMatrix !== undefined ) {\n\n normal.applyMatrix3( normalMatrix ).normalize();\n\n }\n\n faceCopy.vertexNormals.push( normal );\n\n }\n\n faceCopy.color.copy( face.color );\n\n for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {\n\n color = faceVertexColors[ j ];\n faceCopy.vertexColors.push( color.clone() );\n\n }\n\n faceCopy.materialIndex = face.materialIndex + materialIndexOffset;\n\n faces1.push( faceCopy );\n\n }\n\n // uvs\n\n for ( i = 0, il = uvs2.length; i < il; i ++ ) {\n\n var uv = uvs2[ i ], uvCopy = [];\n\n if ( uv === undefined ) {\n\n continue;\n\n }\n\n for ( var j = 0, jl = uv.length; j < jl; j ++ ) {\n\n uvCopy.push( uv[ j ].clone() );\n\n }\n\n uvs1.push( uvCopy );\n\n }\n\n },\n\n mergeMesh: function ( mesh ) {\n\n if ( ! ( mesh && mesh.isMesh ) ) {\n\n console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );\n return;\n\n }\n\n if ( mesh.matrixAutoUpdate ) mesh.updateMatrix();\n\n this.merge( mesh.geometry, mesh.matrix );\n\n },\n\n /*\n * Checks for duplicate vertices with hashmap.\n * Duplicated vertices are removed\n * and faces' vertices are updated.\n */\n\n mergeVertices: function () {\n\n var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)\n var unique = [], changes = [];\n\n var v, key;\n var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001\n var precision = Math.pow( 10, precisionPoints );\n var i, il, face;\n var indices, j, jl;\n\n for ( i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n v = this.vertices[ i ];\n key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );\n\n if ( verticesMap[ key ] === undefined ) {\n\n verticesMap[ key ] = i;\n unique.push( this.vertices[ i ] );\n changes[ i ] = unique.length - 1;\n\n } else {\n\n //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);\n changes[ i ] = changes[ verticesMap[ key ] ];\n\n }\n\n }\n\n\n // if faces are completely degenerate after merging vertices, we\n // have to remove them from the geometry.\n var faceIndicesToRemove = [];\n\n for ( i = 0, il = this.faces.length; i < il; i ++ ) {\n\n face = this.faces[ i ];\n\n face.a = changes[ face.a ];\n face.b = changes[ face.b ];\n face.c = changes[ face.c ];\n\n indices = [ face.a, face.b, face.c ];\n\n // if any duplicate vertices are found in a Face3\n // we have to remove the face as nothing can be saved\n for ( var n = 0; n < 3; n ++ ) {\n\n if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {\n\n faceIndicesToRemove.push( i );\n break;\n\n }\n\n }\n\n }\n\n for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {\n\n var idx = faceIndicesToRemove[ i ];\n\n this.faces.splice( idx, 1 );\n\n for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {\n\n this.faceVertexUvs[ j ].splice( idx, 1 );\n\n }\n\n }\n\n // Use unique set of vertices\n\n var diff = this.vertices.length - unique.length;\n this.vertices = unique;\n return diff;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.vertices = [];\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );\n\n }\n\n return this;\n\n },\n\n sortFacesByMaterialIndex: function () {\n\n var faces = this.faces;\n var length = faces.length;\n\n // tag faces\n\n for ( var i = 0; i < length; i ++ ) {\n\n faces[ i ]._id = i;\n\n }\n\n // sort faces\n\n function materialIndexSort( a, b ) {\n\n return a.materialIndex - b.materialIndex;\n\n }\n\n faces.sort( materialIndexSort );\n\n // sort uvs\n\n var uvs1 = this.faceVertexUvs[ 0 ];\n var uvs2 = this.faceVertexUvs[ 1 ];\n\n var newUvs1, newUvs2;\n\n if ( uvs1 && uvs1.length === length ) newUvs1 = [];\n if ( uvs2 && uvs2.length === length ) newUvs2 = [];\n\n for ( var i = 0; i < length; i ++ ) {\n\n var id = faces[ i ]._id;\n\n if ( newUvs1 ) newUvs1.push( uvs1[ id ] );\n if ( newUvs2 ) newUvs2.push( uvs2[ id ] );\n\n }\n\n if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;\n if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Geometry',\n generator: 'Geometry.toJSON'\n }\n };\n\n // standard Geometry serialization\n\n data.uuid = this.uuid;\n data.type = this.type;\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.parameters !== undefined ) {\n\n var parameters = this.parameters;\n\n for ( var key in parameters ) {\n\n if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n }\n\n return data;\n\n }\n\n var vertices = [];\n\n for ( var i = 0; i < this.vertices.length; i ++ ) {\n\n var vertex = this.vertices[ i ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n var faces = [];\n var normals = [];\n var normalsHash = {};\n var colors = [];\n var colorsHash = {};\n var uvs = [];\n var uvsHash = {};\n\n for ( var i = 0; i < this.faces.length; i ++ ) {\n\n var face = this.faces[ i ];\n\n var hasMaterial = true;\n var hasFaceUv = false; // deprecated\n var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;\n var hasFaceNormal = face.normal.length() > 0;\n var hasFaceVertexNormal = face.vertexNormals.length > 0;\n var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;\n var hasFaceVertexColor = face.vertexColors.length > 0;\n\n var faceType = 0;\n\n faceType = setBit( faceType, 0, 0 ); // isQuad\n faceType = setBit( faceType, 1, hasMaterial );\n faceType = setBit( faceType, 2, hasFaceUv );\n faceType = setBit( faceType, 3, hasFaceVertexUv );\n faceType = setBit( faceType, 4, hasFaceNormal );\n faceType = setBit( faceType, 5, hasFaceVertexNormal );\n faceType = setBit( faceType, 6, hasFaceColor );\n faceType = setBit( faceType, 7, hasFaceVertexColor );\n\n faces.push( faceType );\n faces.push( face.a, face.b, face.c );\n faces.push( face.materialIndex );\n\n if ( hasFaceVertexUv ) {\n\n var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];\n\n faces.push(\n getUvIndex( faceVertexUvs[ 0 ] ),\n getUvIndex( faceVertexUvs[ 1 ] ),\n getUvIndex( faceVertexUvs[ 2 ] )\n );\n\n }\n\n if ( hasFaceNormal ) {\n\n faces.push( getNormalIndex( face.normal ) );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n var vertexNormals = face.vertexNormals;\n\n faces.push(\n getNormalIndex( vertexNormals[ 0 ] ),\n getNormalIndex( vertexNormals[ 1 ] ),\n getNormalIndex( vertexNormals[ 2 ] )\n );\n\n }\n\n if ( hasFaceColor ) {\n\n faces.push( getColorIndex( face.color ) );\n\n }\n\n if ( hasFaceVertexColor ) {\n\n var vertexColors = face.vertexColors;\n\n faces.push(\n getColorIndex( vertexColors[ 0 ] ),\n getColorIndex( vertexColors[ 1 ] ),\n getColorIndex( vertexColors[ 2 ] )\n );\n\n }\n\n }\n\n function setBit( value, position, enabled ) {\n\n return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );\n\n }\n\n function getNormalIndex( normal ) {\n\n var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();\n\n if ( normalsHash[ hash ] !== undefined ) {\n\n return normalsHash[ hash ];\n\n }\n\n normalsHash[ hash ] = normals.length / 3;\n normals.push( normal.x, normal.y, normal.z );\n\n return normalsHash[ hash ];\n\n }\n\n function getColorIndex( color ) {\n\n var hash = color.r.toString() + color.g.toString() + color.b.toString();\n\n if ( colorsHash[ hash ] !== undefined ) {\n\n return colorsHash[ hash ];\n\n }\n\n colorsHash[ hash ] = colors.length;\n colors.push( color.getHex() );\n\n return colorsHash[ hash ];\n\n }\n\n function getUvIndex( uv ) {\n\n var hash = uv.x.toString() + uv.y.toString();\n\n if ( uvsHash[ hash ] !== undefined ) {\n\n return uvsHash[ hash ];\n\n }\n\n uvsHash[ hash ] = uvs.length / 2;\n uvs.push( uv.x, uv.y );\n\n return uvsHash[ hash ];\n\n }\n\n data.data = {};\n\n data.data.vertices = vertices;\n data.data.normals = normals;\n if ( colors.length > 0 ) data.data.colors = colors;\n if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility\n data.data.faces = faces;\n\n return data;\n\n },\n\n clone: function () {\n\n /*\n // Handle primitives\n\n var parameters = this.parameters;\n\n if ( parameters !== undefined ) {\n\n var values = [];\n\n for ( var key in parameters ) {\n\n values.push( parameters[ key ] );\n\n }\n\n var geometry = Object.create( this.constructor.prototype );\n this.constructor.apply( geometry, values );\n return geometry;\n\n }\n\n return new this.constructor().copy( this );\n */\n\n return new Geometry().copy( this );\n\n },\n\n copy: function ( source ) {\n\n var i, il, j, jl, k, kl;\n\n // reset\n\n this.vertices = [];\n this.colors = [];\n this.faces = [];\n this.faceVertexUvs = [[]];\n this.morphTargets = [];\n this.morphNormals = [];\n this.skinWeights = [];\n this.skinIndices = [];\n this.lineDistances = [];\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // name\n\n this.name = source.name;\n\n // vertices\n\n var vertices = source.vertices;\n\n for ( i = 0, il = vertices.length; i < il; i ++ ) {\n\n this.vertices.push( vertices[ i ].clone() );\n\n }\n\n // colors\n\n var colors = source.colors;\n\n for ( i = 0, il = colors.length; i < il; i ++ ) {\n\n this.colors.push( colors[ i ].clone() );\n\n }\n\n // faces\n\n var faces = source.faces;\n\n for ( i = 0, il = faces.length; i < il; i ++ ) {\n\n this.faces.push( faces[ i ].clone() );\n\n }\n\n // face vertex uvs\n\n for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {\n\n var faceVertexUvs = source.faceVertexUvs[ i ];\n\n if ( this.faceVertexUvs[ i ] === undefined ) {\n\n this.faceVertexUvs[ i ] = [];\n\n }\n\n for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {\n\n var uvs = faceVertexUvs[ j ], uvsCopy = [];\n\n for ( k = 0, kl = uvs.length; k < kl; k ++ ) {\n\n var uv = uvs[ k ];\n\n uvsCopy.push( uv.clone() );\n\n }\n\n this.faceVertexUvs[ i ].push( uvsCopy );\n\n }\n\n }\n\n // morph targets\n\n var morphTargets = source.morphTargets;\n\n for ( i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n var morphTarget = {};\n morphTarget.name = morphTargets[ i ].name;\n\n // vertices\n\n if ( morphTargets[ i ].vertices !== undefined ) {\n\n morphTarget.vertices = [];\n\n for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) {\n\n morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() );\n\n }\n\n }\n\n // normals\n\n if ( morphTargets[ i ].normals !== undefined ) {\n\n morphTarget.normals = [];\n\n for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) {\n\n morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() );\n\n }\n\n }\n\n this.morphTargets.push( morphTarget );\n\n }\n\n // morph normals\n\n var morphNormals = source.morphNormals;\n\n for ( i = 0, il = morphNormals.length; i < il; i ++ ) {\n\n var morphNormal = {};\n\n // vertex normals\n\n if ( morphNormals[ i ].vertexNormals !== undefined ) {\n\n morphNormal.vertexNormals = [];\n\n for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) {\n\n var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ];\n var destVertexNormal = {};\n\n destVertexNormal.a = srcVertexNormal.a.clone();\n destVertexNormal.b = srcVertexNormal.b.clone();\n destVertexNormal.c = srcVertexNormal.c.clone();\n\n morphNormal.vertexNormals.push( destVertexNormal );\n\n }\n\n }\n\n // face normals\n\n if ( morphNormals[ i ].faceNormals !== undefined ) {\n\n morphNormal.faceNormals = [];\n\n for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) {\n\n morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() );\n\n }\n\n }\n\n this.morphNormals.push( morphNormal );\n\n }\n\n // skin weights\n\n var skinWeights = source.skinWeights;\n\n for ( i = 0, il = skinWeights.length; i < il; i ++ ) {\n\n this.skinWeights.push( skinWeights[ i ].clone() );\n\n }\n\n // skin indices\n\n var skinIndices = source.skinIndices;\n\n for ( i = 0, il = skinIndices.length; i < il; i ++ ) {\n\n this.skinIndices.push( skinIndices[ i ].clone() );\n\n }\n\n // line distances\n\n var lineDistances = source.lineDistances;\n\n for ( i = 0, il = lineDistances.length; i < il; i ++ ) {\n\n this.lineDistances.push( lineDistances[ i ] );\n\n }\n\n // bounding box\n\n var boundingBox = source.boundingBox;\n\n if ( boundingBox !== null ) {\n\n this.boundingBox = boundingBox.clone();\n\n }\n\n // bounding sphere\n\n var boundingSphere = source.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n this.boundingSphere = boundingSphere.clone();\n\n }\n\n // update flags\n\n this.elementsNeedUpdate = source.elementsNeedUpdate;\n this.verticesNeedUpdate = source.verticesNeedUpdate;\n this.uvsNeedUpdate = source.uvsNeedUpdate;\n this.normalsNeedUpdate = source.normalsNeedUpdate;\n this.colorsNeedUpdate = source.colorsNeedUpdate;\n this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate;\n this.groupsNeedUpdate = source.groupsNeedUpdate;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function BufferAttribute( array, itemSize, normalized ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.name = '';\n\n this.array = array;\n this.itemSize = itemSize;\n this.count = array !== undefined ? array.length / itemSize : 0;\n this.normalized = normalized === true;\n\n this.dynamic = false;\n this.updateRange = { offset: 0, count: - 1 };\n\n this.version = 0;\n\n }\n\n Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n Object.assign( BufferAttribute.prototype, {\n\n isBufferAttribute: true,\n\n onUploadCallback: function () {},\n\n setArray: function ( array ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.count = array !== undefined ? array.length / this.itemSize : 0;\n this.array = array;\n\n },\n\n setDynamic: function ( value ) {\n\n this.dynamic = value;\n\n return this;\n\n },\n\n copy: function ( source ) {\n\n this.array = new source.array.constructor( source.array );\n this.itemSize = source.itemSize;\n this.count = source.count;\n this.normalized = source.normalized;\n\n this.dynamic = source.dynamic;\n\n return this;\n\n },\n\n copyAt: function ( index1, attribute, index2 ) {\n\n index1 *= this.itemSize;\n index2 *= attribute.itemSize;\n\n for ( var i = 0, l = this.itemSize; i < l; i ++ ) {\n\n this.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n }\n\n return this;\n\n },\n\n copyArray: function ( array ) {\n\n this.array.set( array );\n\n return this;\n\n },\n\n copyColorsArray: function ( colors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = colors.length; i < l; i ++ ) {\n\n var color = colors[ i ];\n\n if ( color === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );\n color = new Color();\n\n }\n\n array[ offset ++ ] = color.r;\n array[ offset ++ ] = color.g;\n array[ offset ++ ] = color.b;\n\n }\n\n return this;\n\n },\n\n copyVector2sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );\n vector = new Vector2();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n\n }\n\n return this;\n\n },\n\n copyVector3sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );\n vector = new Vector3();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n array[ offset ++ ] = vector.z;\n\n }\n\n return this;\n\n },\n\n copyVector4sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );\n vector = new Vector4();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n array[ offset ++ ] = vector.z;\n array[ offset ++ ] = vector.w;\n\n }\n\n return this;\n\n },\n\n set: function ( value, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.array.set( value, offset );\n\n return this;\n\n },\n\n getX: function ( index ) {\n\n return this.array[ index * this.itemSize ];\n\n },\n\n setX: function ( index, x ) {\n\n this.array[ index * this.itemSize ] = x;\n\n return this;\n\n },\n\n getY: function ( index ) {\n\n return this.array[ index * this.itemSize + 1 ];\n\n },\n\n setY: function ( index, y ) {\n\n this.array[ index * this.itemSize + 1 ] = y;\n\n return this;\n\n },\n\n getZ: function ( index ) {\n\n return this.array[ index * this.itemSize + 2 ];\n\n },\n\n setZ: function ( index, z ) {\n\n this.array[ index * this.itemSize + 2 ] = z;\n\n return this;\n\n },\n\n getW: function ( index ) {\n\n return this.array[ index * this.itemSize + 3 ];\n\n },\n\n setW: function ( index, w ) {\n\n this.array[ index * this.itemSize + 3 ] = w;\n\n return this;\n\n },\n\n setXY: function ( index, x, y ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n\n return this;\n\n },\n\n setXYZ: function ( index, x, y, z ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n this.array[ index + 2 ] = z;\n\n return this;\n\n },\n\n setXYZW: function ( index, x, y, z, w ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n this.array[ index + 2 ] = z;\n this.array[ index + 3 ] = w;\n\n return this;\n\n },\n\n onUpload: function ( callback ) {\n\n this.onUploadCallback = callback;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this.array, this.itemSize ).copy( this );\n\n }\n\n } );\n\n //\n\n function Int8BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized );\n\n }\n\n Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int8BufferAttribute.prototype.constructor = Int8BufferAttribute;\n\n\n function Uint8BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized );\n\n }\n\n Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute;\n\n\n function Uint8ClampedBufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized );\n\n }\n\n Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute;\n\n\n function Int16BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized );\n\n }\n\n Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int16BufferAttribute.prototype.constructor = Int16BufferAttribute;\n\n\n function Uint16BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );\n\n }\n\n Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute;\n\n\n function Int32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized );\n\n }\n\n Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int32BufferAttribute.prototype.constructor = Int32BufferAttribute;\n\n\n function Uint32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized );\n\n }\n\n Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute;\n\n\n function Float32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized );\n\n }\n\n Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Float32BufferAttribute.prototype.constructor = Float32BufferAttribute;\n\n\n function Float64BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized );\n\n }\n\n Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Float64BufferAttribute.prototype.constructor = Float64BufferAttribute;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function DirectGeometry() {\n\n this.vertices = [];\n this.normals = [];\n this.colors = [];\n this.uvs = [];\n this.uvs2 = [];\n\n this.groups = [];\n\n this.morphTargets = {};\n\n this.skinWeights = [];\n this.skinIndices = [];\n\n // this.lineDistances = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // update flags\n\n this.verticesNeedUpdate = false;\n this.normalsNeedUpdate = false;\n this.colorsNeedUpdate = false;\n this.uvsNeedUpdate = false;\n this.groupsNeedUpdate = false;\n\n }\n\n Object.assign( DirectGeometry.prototype, {\n\n computeGroups: function ( geometry ) {\n\n var group;\n var groups = [];\n var materialIndex = undefined;\n\n var faces = geometry.faces;\n\n for ( var i = 0; i < faces.length; i ++ ) {\n\n var face = faces[ i ];\n\n // materials\n\n if ( face.materialIndex !== materialIndex ) {\n\n materialIndex = face.materialIndex;\n\n if ( group !== undefined ) {\n\n group.count = ( i * 3 ) - group.start;\n groups.push( group );\n\n }\n\n group = {\n start: i * 3,\n materialIndex: materialIndex\n };\n\n }\n\n }\n\n if ( group !== undefined ) {\n\n group.count = ( i * 3 ) - group.start;\n groups.push( group );\n\n }\n\n this.groups = groups;\n\n },\n\n fromGeometry: function ( geometry ) {\n\n var faces = geometry.faces;\n var vertices = geometry.vertices;\n var faceVertexUvs = geometry.faceVertexUvs;\n\n var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;\n var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;\n\n // morphs\n\n var morphTargets = geometry.morphTargets;\n var morphTargetsLength = morphTargets.length;\n\n var morphTargetsPosition;\n\n if ( morphTargetsLength > 0 ) {\n\n morphTargetsPosition = [];\n\n for ( var i = 0; i < morphTargetsLength; i ++ ) {\n\n morphTargetsPosition[ i ] = [];\n\n }\n\n this.morphTargets.position = morphTargetsPosition;\n\n }\n\n var morphNormals = geometry.morphNormals;\n var morphNormalsLength = morphNormals.length;\n\n var morphTargetsNormal;\n\n if ( morphNormalsLength > 0 ) {\n\n morphTargetsNormal = [];\n\n for ( var i = 0; i < morphNormalsLength; i ++ ) {\n\n morphTargetsNormal[ i ] = [];\n\n }\n\n this.morphTargets.normal = morphTargetsNormal;\n\n }\n\n // skins\n\n var skinIndices = geometry.skinIndices;\n var skinWeights = geometry.skinWeights;\n\n var hasSkinIndices = skinIndices.length === vertices.length;\n var hasSkinWeights = skinWeights.length === vertices.length;\n\n //\n\n for ( var i = 0; i < faces.length; i ++ ) {\n\n var face = faces[ i ];\n\n this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );\n\n } else {\n\n var normal = face.normal;\n\n this.normals.push( normal, normal, normal );\n\n }\n\n var vertexColors = face.vertexColors;\n\n if ( vertexColors.length === 3 ) {\n\n this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );\n\n } else {\n\n var color = face.color;\n\n this.colors.push( color, color, color );\n\n }\n\n if ( hasFaceVertexUv === true ) {\n\n var vertexUvs = faceVertexUvs[ 0 ][ i ];\n\n if ( vertexUvs !== undefined ) {\n\n this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n } else {\n\n console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );\n\n this.uvs.push( new Vector2(), new Vector2(), new Vector2() );\n\n }\n\n }\n\n if ( hasFaceVertexUv2 === true ) {\n\n var vertexUvs = faceVertexUvs[ 1 ][ i ];\n\n if ( vertexUvs !== undefined ) {\n\n this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n } else {\n\n console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );\n\n this.uvs2.push( new Vector2(), new Vector2(), new Vector2() );\n\n }\n\n }\n\n // morphs\n\n for ( var j = 0; j < morphTargetsLength; j ++ ) {\n\n var morphTarget = morphTargets[ j ].vertices;\n\n morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );\n\n }\n\n for ( var j = 0; j < morphNormalsLength; j ++ ) {\n\n var morphNormal = morphNormals[ j ].vertexNormals[ i ];\n\n morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );\n\n }\n\n // skins\n\n if ( hasSkinIndices ) {\n\n this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );\n\n }\n\n if ( hasSkinWeights ) {\n\n this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );\n\n }\n\n }\n\n this.computeGroups( geometry );\n\n this.verticesNeedUpdate = geometry.verticesNeedUpdate;\n this.normalsNeedUpdate = geometry.normalsNeedUpdate;\n this.colorsNeedUpdate = geometry.colorsNeedUpdate;\n this.uvsNeedUpdate = geometry.uvsNeedUpdate;\n this.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function arrayMax( array ) {\n\n if ( array.length === 0 ) return - Infinity;\n\n var max = array[ 0 ];\n\n for ( var i = 1, l = array.length; i < l; ++ i ) {\n\n if ( array[ i ] > max ) max = array[ i ];\n\n }\n\n return max;\n\n }\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id\n\n function BufferGeometry() {\n\n Object.defineProperty( this, 'id', { value: bufferGeometryId += 2 } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'BufferGeometry';\n\n this.index = null;\n this.attributes = {};\n\n this.morphAttributes = {};\n\n this.groups = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n this.drawRange = { start: 0, count: Infinity };\n\n }\n\n BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: BufferGeometry,\n\n isBufferGeometry: true,\n\n getIndex: function () {\n\n return this.index;\n\n },\n\n setIndex: function ( index ) {\n\n if ( Array.isArray( index ) ) {\n\n this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );\n\n } else {\n\n this.index = index;\n\n }\n\n },\n\n addAttribute: function ( name, attribute ) {\n\n if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) {\n\n console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );\n\n this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );\n\n return;\n\n }\n\n if ( name === 'index' ) {\n\n console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );\n this.setIndex( attribute );\n\n return;\n\n }\n\n this.attributes[ name ] = attribute;\n\n return this;\n\n },\n\n getAttribute: function ( name ) {\n\n return this.attributes[ name ];\n\n },\n\n removeAttribute: function ( name ) {\n\n delete this.attributes[ name ];\n\n return this;\n\n },\n\n addGroup: function ( start, count, materialIndex ) {\n\n this.groups.push( {\n\n start: start,\n count: count,\n materialIndex: materialIndex !== undefined ? materialIndex : 0\n\n } );\n\n },\n\n clearGroups: function () {\n\n this.groups = [];\n\n },\n\n setDrawRange: function ( start, count ) {\n\n this.drawRange.start = start;\n this.drawRange.count = count;\n\n },\n\n applyMatrix: function ( matrix ) {\n\n var position = this.attributes.position;\n\n if ( position !== undefined ) {\n\n matrix.applyToBufferAttribute( position );\n position.needsUpdate = true;\n\n }\n\n var normal = this.attributes.normal;\n\n if ( normal !== undefined ) {\n\n var normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n normalMatrix.applyToBufferAttribute( normal );\n normal.needsUpdate = true;\n\n }\n\n if ( this.boundingBox !== null ) {\n\n this.computeBoundingBox();\n\n }\n\n if ( this.boundingSphere !== null ) {\n\n this.computeBoundingSphere();\n\n }\n\n return this;\n\n },\n\n rotateX: function () {\n\n // rotate geometry around world x-axis\n\n var m1 = new Matrix4();\n\n return function rotateX( angle ) {\n\n m1.makeRotationX( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateY: function () {\n\n // rotate geometry around world y-axis\n\n var m1 = new Matrix4();\n\n return function rotateY( angle ) {\n\n m1.makeRotationY( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateZ: function () {\n\n // rotate geometry around world z-axis\n\n var m1 = new Matrix4();\n\n return function rotateZ( angle ) {\n\n m1.makeRotationZ( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n translate: function () {\n\n // translate geometry\n\n var m1 = new Matrix4();\n\n return function translate( x, y, z ) {\n\n m1.makeTranslation( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n scale: function () {\n\n // scale geometry\n\n var m1 = new Matrix4();\n\n return function scale( x, y, z ) {\n\n m1.makeScale( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n lookAt: function () {\n\n var obj = new Object3D();\n\n return function lookAt( vector ) {\n\n obj.lookAt( vector );\n\n obj.updateMatrix();\n\n this.applyMatrix( obj.matrix );\n\n };\n\n }(),\n\n center: function () {\n\n var offset = new Vector3();\n\n return function center() {\n\n this.computeBoundingBox();\n\n this.boundingBox.getCenter( offset ).negate();\n\n this.translate( offset.x, offset.y, offset.z );\n\n return this;\n\n };\n\n }(),\n\n setFromObject: function ( object ) {\n\n // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );\n\n var geometry = object.geometry;\n\n if ( object.isPoints || object.isLine ) {\n\n var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );\n var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );\n\n this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );\n this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );\n\n if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {\n\n var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );\n\n this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );\n\n }\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n } else if ( object.isMesh ) {\n\n if ( geometry && geometry.isGeometry ) {\n\n this.fromGeometry( geometry );\n\n }\n\n }\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n var position = [];\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n position.push( point.x, point.y, point.z || 0 );\n\n }\n\n this.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) );\n\n return this;\n\n },\n\n updateFromObject: function ( object ) {\n\n var geometry = object.geometry;\n\n if ( object.isMesh ) {\n\n var direct = geometry.__directGeometry;\n\n if ( geometry.elementsNeedUpdate === true ) {\n\n direct = undefined;\n geometry.elementsNeedUpdate = false;\n\n }\n\n if ( direct === undefined ) {\n\n return this.fromGeometry( geometry );\n\n }\n\n direct.verticesNeedUpdate = geometry.verticesNeedUpdate;\n direct.normalsNeedUpdate = geometry.normalsNeedUpdate;\n direct.colorsNeedUpdate = geometry.colorsNeedUpdate;\n direct.uvsNeedUpdate = geometry.uvsNeedUpdate;\n direct.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n geometry.verticesNeedUpdate = false;\n geometry.normalsNeedUpdate = false;\n geometry.colorsNeedUpdate = false;\n geometry.uvsNeedUpdate = false;\n geometry.groupsNeedUpdate = false;\n\n geometry = direct;\n\n }\n\n var attribute;\n\n if ( geometry.verticesNeedUpdate === true ) {\n\n attribute = this.attributes.position;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector3sArray( geometry.vertices );\n attribute.needsUpdate = true;\n\n }\n\n geometry.verticesNeedUpdate = false;\n\n }\n\n if ( geometry.normalsNeedUpdate === true ) {\n\n attribute = this.attributes.normal;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector3sArray( geometry.normals );\n attribute.needsUpdate = true;\n\n }\n\n geometry.normalsNeedUpdate = false;\n\n }\n\n if ( geometry.colorsNeedUpdate === true ) {\n\n attribute = this.attributes.color;\n\n if ( attribute !== undefined ) {\n\n attribute.copyColorsArray( geometry.colors );\n attribute.needsUpdate = true;\n\n }\n\n geometry.colorsNeedUpdate = false;\n\n }\n\n if ( geometry.uvsNeedUpdate ) {\n\n attribute = this.attributes.uv;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector2sArray( geometry.uvs );\n attribute.needsUpdate = true;\n\n }\n\n geometry.uvsNeedUpdate = false;\n\n }\n\n if ( geometry.lineDistancesNeedUpdate ) {\n\n attribute = this.attributes.lineDistance;\n\n if ( attribute !== undefined ) {\n\n attribute.copyArray( geometry.lineDistances );\n attribute.needsUpdate = true;\n\n }\n\n geometry.lineDistancesNeedUpdate = false;\n\n }\n\n if ( geometry.groupsNeedUpdate ) {\n\n geometry.computeGroups( object.geometry );\n this.groups = geometry.groups;\n\n geometry.groupsNeedUpdate = false;\n\n }\n\n return this;\n\n },\n\n fromGeometry: function ( geometry ) {\n\n geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );\n\n return this.fromDirectGeometry( geometry.__directGeometry );\n\n },\n\n fromDirectGeometry: function ( geometry ) {\n\n var positions = new Float32Array( geometry.vertices.length * 3 );\n this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );\n\n if ( geometry.normals.length > 0 ) {\n\n var normals = new Float32Array( geometry.normals.length * 3 );\n this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );\n\n }\n\n if ( geometry.colors.length > 0 ) {\n\n var colors = new Float32Array( geometry.colors.length * 3 );\n this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );\n\n }\n\n if ( geometry.uvs.length > 0 ) {\n\n var uvs = new Float32Array( geometry.uvs.length * 2 );\n this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );\n\n }\n\n if ( geometry.uvs2.length > 0 ) {\n\n var uvs2 = new Float32Array( geometry.uvs2.length * 2 );\n this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );\n\n }\n\n // groups\n\n this.groups = geometry.groups;\n\n // morphs\n\n for ( var name in geometry.morphTargets ) {\n\n var array = [];\n var morphTargets = geometry.morphTargets[ name ];\n\n for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {\n\n var morphTarget = morphTargets[ i ];\n\n var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 );\n\n array.push( attribute.copyVector3sArray( morphTarget ) );\n\n }\n\n this.morphAttributes[ name ] = array;\n\n }\n\n // skinning\n\n if ( geometry.skinIndices.length > 0 ) {\n\n var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );\n this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );\n\n }\n\n if ( geometry.skinWeights.length > 0 ) {\n\n var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );\n this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );\n\n }\n\n //\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n return this;\n\n },\n\n computeBoundingBox: function () {\n\n if ( this.boundingBox === null ) {\n\n this.boundingBox = new Box3();\n\n }\n\n var position = this.attributes.position;\n\n if ( position !== undefined ) {\n\n this.boundingBox.setFromBufferAttribute( position );\n\n } else {\n\n this.boundingBox.makeEmpty();\n\n }\n\n if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {\n\n console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this );\n\n }\n\n },\n\n computeBoundingSphere: function () {\n\n var box = new Box3();\n var vector = new Vector3();\n\n return function computeBoundingSphere() {\n\n if ( this.boundingSphere === null ) {\n\n this.boundingSphere = new Sphere();\n\n }\n\n var position = this.attributes.position;\n\n if ( position ) {\n\n var center = this.boundingSphere.center;\n\n box.setFromBufferAttribute( position );\n box.getCenter( center );\n\n // hoping to find a boundingSphere with a radius smaller than the\n // boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n var maxRadiusSq = 0;\n\n for ( var i = 0, il = position.count; i < il; i ++ ) {\n\n vector.x = position.getX( i );\n vector.y = position.getY( i );\n vector.z = position.getZ( i );\n maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );\n\n }\n\n this.boundingSphere.radius = Math.sqrt( maxRadiusSq );\n\n if ( isNaN( this.boundingSphere.radius ) ) {\n\n console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this );\n\n }\n\n }\n\n };\n\n }(),\n\n computeFaceNormals: function () {\n\n // backwards compatibility\n\n },\n\n computeVertexNormals: function () {\n\n var index = this.index;\n var attributes = this.attributes;\n var groups = this.groups;\n\n if ( attributes.position ) {\n\n var positions = attributes.position.array;\n\n if ( attributes.normal === undefined ) {\n\n this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) );\n\n } else {\n\n // reset existing normals to zero\n\n var array = attributes.normal.array;\n\n for ( var i = 0, il = array.length; i < il; i ++ ) {\n\n array[ i ] = 0;\n\n }\n\n }\n\n var normals = attributes.normal.array;\n\n var vA, vB, vC;\n var pA = new Vector3(), pB = new Vector3(), pC = new Vector3();\n var cb = new Vector3(), ab = new Vector3();\n\n // indexed elements\n\n if ( index ) {\n\n var indices = index.array;\n\n if ( groups.length === 0 ) {\n\n this.addGroup( 0, indices.length );\n\n }\n\n for ( var j = 0, jl = groups.length; j < jl; ++ j ) {\n\n var group = groups[ j ];\n\n var start = group.start;\n var count = group.count;\n\n for ( var i = start, il = start + count; i < il; i += 3 ) {\n\n vA = indices[ i + 0 ] * 3;\n vB = indices[ i + 1 ] * 3;\n vC = indices[ i + 2 ] * 3;\n\n pA.fromArray( positions, vA );\n pB.fromArray( positions, vB );\n pC.fromArray( positions, vC );\n\n cb.subVectors( pC, pB );\n ab.subVectors( pA, pB );\n cb.cross( ab );\n\n normals[ vA ] += cb.x;\n normals[ vA + 1 ] += cb.y;\n normals[ vA + 2 ] += cb.z;\n\n normals[ vB ] += cb.x;\n normals[ vB + 1 ] += cb.y;\n normals[ vB + 2 ] += cb.z;\n\n normals[ vC ] += cb.x;\n normals[ vC + 1 ] += cb.y;\n normals[ vC + 2 ] += cb.z;\n\n }\n\n }\n\n } else {\n\n // non-indexed elements (unconnected triangle soup)\n\n for ( var i = 0, il = positions.length; i < il; i += 9 ) {\n\n pA.fromArray( positions, i );\n pB.fromArray( positions, i + 3 );\n pC.fromArray( positions, i + 6 );\n\n cb.subVectors( pC, pB );\n ab.subVectors( pA, pB );\n cb.cross( ab );\n\n normals[ i ] = cb.x;\n normals[ i + 1 ] = cb.y;\n normals[ i + 2 ] = cb.z;\n\n normals[ i + 3 ] = cb.x;\n normals[ i + 4 ] = cb.y;\n normals[ i + 5 ] = cb.z;\n\n normals[ i + 6 ] = cb.x;\n normals[ i + 7 ] = cb.y;\n normals[ i + 8 ] = cb.z;\n\n }\n\n }\n\n this.normalizeNormals();\n\n attributes.normal.needsUpdate = true;\n\n }\n\n },\n\n merge: function ( geometry, offset ) {\n\n if ( ! ( geometry && geometry.isBufferGeometry ) ) {\n\n console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );\n return;\n\n }\n\n if ( offset === undefined ) {\n\n offset = 0;\n\n console.warn(\n 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '\n + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'\n );\n\n }\n\n var attributes = this.attributes;\n\n for ( var key in attributes ) {\n\n if ( geometry.attributes[ key ] === undefined ) continue;\n\n var attribute1 = attributes[ key ];\n var attributeArray1 = attribute1.array;\n\n var attribute2 = geometry.attributes[ key ];\n var attributeArray2 = attribute2.array;\n\n var attributeSize = attribute2.itemSize;\n\n for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {\n\n attributeArray1[ j ] = attributeArray2[ i ];\n\n }\n\n }\n\n return this;\n\n },\n\n normalizeNormals: function () {\n\n var vector = new Vector3();\n\n return function normalizeNormals() {\n\n var normals = this.attributes.normal;\n\n for ( var i = 0, il = normals.count; i < il; i ++ ) {\n\n vector.x = normals.getX( i );\n vector.y = normals.getY( i );\n vector.z = normals.getZ( i );\n\n vector.normalize();\n\n normals.setXYZ( i, vector.x, vector.y, vector.z );\n\n }\n\n };\n\n }(),\n\n toNonIndexed: function () {\n\n if ( this.index === null ) {\n\n console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );\n return this;\n\n }\n\n var geometry2 = new BufferGeometry();\n\n var indices = this.index.array;\n var attributes = this.attributes;\n\n for ( var name in attributes ) {\n\n var attribute = attributes[ name ];\n\n var array = attribute.array;\n var itemSize = attribute.itemSize;\n\n var array2 = new array.constructor( indices.length * itemSize );\n\n var index = 0, index2 = 0;\n\n for ( var i = 0, l = indices.length; i < l; i ++ ) {\n\n index = indices[ i ] * itemSize;\n\n for ( var j = 0; j < itemSize; j ++ ) {\n\n array2[ index2 ++ ] = array[ index ++ ];\n\n }\n\n }\n\n geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) );\n\n }\n\n var groups = this.groups;\n\n for ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n geometry2.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n return geometry2;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'BufferGeometry',\n generator: 'BufferGeometry.toJSON'\n }\n };\n\n // standard BufferGeometry serialization\n\n data.uuid = this.uuid;\n data.type = this.type;\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.parameters !== undefined ) {\n\n var parameters = this.parameters;\n\n for ( var key in parameters ) {\n\n if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n }\n\n return data;\n\n }\n\n data.data = { attributes: {} };\n\n var index = this.index;\n\n if ( index !== null ) {\n\n var array = Array.prototype.slice.call( index.array );\n\n data.data.index = {\n type: index.array.constructor.name,\n array: array\n };\n\n }\n\n var attributes = this.attributes;\n\n for ( var key in attributes ) {\n\n var attribute = attributes[ key ];\n\n var array = Array.prototype.slice.call( attribute.array );\n\n data.data.attributes[ key ] = {\n itemSize: attribute.itemSize,\n type: attribute.array.constructor.name,\n array: array,\n normalized: attribute.normalized\n };\n\n }\n\n var groups = this.groups;\n\n if ( groups.length > 0 ) {\n\n data.data.groups = JSON.parse( JSON.stringify( groups ) );\n\n }\n\n var boundingSphere = this.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n data.data.boundingSphere = {\n center: boundingSphere.center.toArray(),\n radius: boundingSphere.radius\n };\n\n }\n\n return data;\n\n },\n\n clone: function () {\n\n /*\n // Handle primitives\n\n var parameters = this.parameters;\n\n if ( parameters !== undefined ) {\n\n var values = [];\n\n for ( var key in parameters ) {\n\n values.push( parameters[ key ] );\n\n }\n\n var geometry = Object.create( this.constructor.prototype );\n this.constructor.apply( geometry, values );\n return geometry;\n\n }\n\n return new this.constructor().copy( this );\n */\n\n return new BufferGeometry().copy( this );\n\n },\n\n copy: function ( source ) {\n\n var name, i, l;\n\n // reset\n\n this.index = null;\n this.attributes = {};\n this.morphAttributes = {};\n this.groups = [];\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // name\n\n this.name = source.name;\n\n // index\n\n var index = source.index;\n\n if ( index !== null ) {\n\n this.setIndex( index.clone() );\n\n }\n\n // attributes\n\n var attributes = source.attributes;\n\n for ( name in attributes ) {\n\n var attribute = attributes[ name ];\n this.addAttribute( name, attribute.clone() );\n\n }\n\n // morph attributes\n\n var morphAttributes = source.morphAttributes;\n\n for ( name in morphAttributes ) {\n\n var array = [];\n var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n for ( i = 0, l = morphAttribute.length; i < l; i ++ ) {\n\n array.push( morphAttribute[ i ].clone() );\n\n }\n\n this.morphAttributes[ name ] = array;\n\n }\n\n // groups\n\n var groups = source.groups;\n\n for ( i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n this.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n // bounding box\n\n var boundingBox = source.boundingBox;\n\n if ( boundingBox !== null ) {\n\n this.boundingBox = boundingBox.clone();\n\n }\n\n // bounding sphere\n\n var boundingSphere = source.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n this.boundingSphere = boundingSphere.clone();\n\n }\n\n // draw range\n\n this.drawRange.start = source.drawRange.start;\n this.drawRange.count = source.drawRange.count;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // BoxGeometry\n\n function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n Geometry.call( this );\n\n this.type = 'BoxGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n depth: depth,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n depthSegments: depthSegments\n };\n\n this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) );\n this.mergeVertices();\n\n }\n\n BoxGeometry.prototype = Object.create( Geometry.prototype );\n BoxGeometry.prototype.constructor = BoxGeometry;\n\n // BoxBufferGeometry\n\n function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'BoxBufferGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n depth: depth,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n depthSegments: depthSegments\n };\n\n var scope = this;\n\n width = width || 1;\n height = height || 1;\n depth = depth || 1;\n\n // segments\n\n widthSegments = Math.floor( widthSegments ) || 1;\n heightSegments = Math.floor( heightSegments ) || 1;\n depthSegments = Math.floor( depthSegments ) || 1;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var numberOfVertices = 0;\n var groupStart = 0;\n\n // build each side of the box geometry\n\n buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px\n buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx\n buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py\n buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny\n buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz\n buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {\n\n var segmentWidth = width / gridX;\n var segmentHeight = height / gridY;\n\n var widthHalf = width / 2;\n var heightHalf = height / 2;\n var depthHalf = depth / 2;\n\n var gridX1 = gridX + 1;\n var gridY1 = gridY + 1;\n\n var vertexCounter = 0;\n var groupCount = 0;\n\n var ix, iy;\n\n var vector = new Vector3();\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy < gridY1; iy ++ ) {\n\n var y = iy * segmentHeight - heightHalf;\n\n for ( ix = 0; ix < gridX1; ix ++ ) {\n\n var x = ix * segmentWidth - widthHalf;\n\n // set values to correct vector component\n\n vector[ u ] = x * udir;\n vector[ v ] = y * vdir;\n vector[ w ] = depthHalf;\n\n // now apply vector to vertex buffer\n\n vertices.push( vector.x, vector.y, vector.z );\n\n // set values to correct vector component\n\n vector[ u ] = 0;\n vector[ v ] = 0;\n vector[ w ] = depth > 0 ? 1 : - 1;\n\n // now apply vector to normal buffer\n\n normals.push( vector.x, vector.y, vector.z );\n\n // uvs\n\n uvs.push( ix / gridX );\n uvs.push( 1 - ( iy / gridY ) );\n\n // counters\n\n vertexCounter += 1;\n\n }\n\n }\n\n // indices\n\n // 1. you need three indices to draw a single face\n // 2. a single segment consists of two faces\n // 3. so we need to generate six (2*3) indices per segment\n\n for ( iy = 0; iy < gridY; iy ++ ) {\n\n for ( ix = 0; ix < gridX; ix ++ ) {\n\n var a = numberOfVertices + ix + gridX1 * iy;\n var b = numberOfVertices + ix + gridX1 * ( iy + 1 );\n var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );\n var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n // increase counter\n\n groupCount += 6;\n\n }\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, materialIndex );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n // update total number of vertices\n\n numberOfVertices += vertexCounter;\n\n }\n\n }\n\n BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n BoxBufferGeometry.prototype.constructor = BoxBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // PlaneGeometry\n\n function PlaneGeometry( width, height, widthSegments, heightSegments ) {\n\n Geometry.call( this );\n\n this.type = 'PlaneGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n widthSegments: widthSegments,\n heightSegments: heightSegments\n };\n\n this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );\n this.mergeVertices();\n\n }\n\n PlaneGeometry.prototype = Object.create( Geometry.prototype );\n PlaneGeometry.prototype.constructor = PlaneGeometry;\n\n // PlaneBufferGeometry\n\n function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'PlaneBufferGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n widthSegments: widthSegments,\n heightSegments: heightSegments\n };\n\n width = width || 1;\n height = height || 1;\n\n var width_half = width / 2;\n var height_half = height / 2;\n\n var gridX = Math.floor( widthSegments ) || 1;\n var gridY = Math.floor( heightSegments ) || 1;\n\n var gridX1 = gridX + 1;\n var gridY1 = gridY + 1;\n\n var segment_width = width / gridX;\n var segment_height = height / gridY;\n\n var ix, iy;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy < gridY1; iy ++ ) {\n\n var y = iy * segment_height - height_half;\n\n for ( ix = 0; ix < gridX1; ix ++ ) {\n\n var x = ix * segment_width - width_half;\n\n vertices.push( x, - y, 0 );\n\n normals.push( 0, 0, 1 );\n\n uvs.push( ix / gridX );\n uvs.push( 1 - ( iy / gridY ) );\n\n }\n\n }\n\n // indices\n\n for ( iy = 0; iy < gridY; iy ++ ) {\n\n for ( ix = 0; ix < gridX; ix ++ ) {\n\n var a = ix + gridX1 * iy;\n var b = ix + gridX1 * ( iy + 1 );\n var c = ( ix + 1 ) + gridX1 * ( iy + 1 );\n var d = ( ix + 1 ) + gridX1 * iy;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n var materialId = 0;\n\n function Material() {\n\n Object.defineProperty( this, 'id', { value: materialId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Material';\n\n this.fog = true;\n this.lights = true;\n\n this.blending = NormalBlending;\n this.side = FrontSide;\n this.flatShading = false;\n this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors\n\n this.opacity = 1;\n this.transparent = false;\n\n this.blendSrc = SrcAlphaFactor;\n this.blendDst = OneMinusSrcAlphaFactor;\n this.blendEquation = AddEquation;\n this.blendSrcAlpha = null;\n this.blendDstAlpha = null;\n this.blendEquationAlpha = null;\n\n this.depthFunc = LessEqualDepth;\n this.depthTest = true;\n this.depthWrite = true;\n\n this.clippingPlanes = null;\n this.clipIntersection = false;\n this.clipShadows = false;\n\n this.shadowSide = null;\n\n this.colorWrite = true;\n\n this.precision = null; // override the renderer's default precision for this material\n\n this.polygonOffset = false;\n this.polygonOffsetFactor = 0;\n this.polygonOffsetUnits = 0;\n\n this.dithering = false;\n\n this.alphaTest = 0;\n this.premultipliedAlpha = false;\n\n this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer\n\n this.visible = true;\n\n this.userData = {};\n\n this.needsUpdate = true;\n\n }\n\n Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Material,\n\n isMaterial: true,\n\n onBeforeCompile: function () {},\n\n setValues: function ( values ) {\n\n if ( values === undefined ) return;\n\n for ( var key in values ) {\n\n var newValue = values[ key ];\n\n if ( newValue === undefined ) {\n\n console.warn( \"THREE.Material: '\" + key + \"' parameter is undefined.\" );\n continue;\n\n }\n\n // for backward compatability if shading is set in the constructor\n if ( key === 'shading' ) {\n\n console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n this.flatShading = ( newValue === FlatShading ) ? true : false;\n continue;\n\n }\n\n var currentValue = this[ key ];\n\n if ( currentValue === undefined ) {\n\n console.warn( \"THREE.\" + this.type + \": '\" + key + \"' is not a property of this material.\" );\n continue;\n\n }\n\n if ( currentValue && currentValue.isColor ) {\n\n currentValue.set( newValue );\n\n } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {\n\n currentValue.copy( newValue );\n\n } else if ( key === 'overdraw' ) {\n\n // ensure overdraw is backwards-compatible with legacy boolean type\n this[ key ] = Number( newValue );\n\n } else {\n\n this[ key ] = newValue;\n\n }\n\n }\n\n },\n\n toJSON: function ( meta ) {\n\n var isRoot = ( meta === undefined || typeof meta === 'string' );\n\n if ( isRoot ) {\n\n meta = {\n textures: {},\n images: {}\n };\n\n }\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Material',\n generator: 'Material.toJSON'\n }\n };\n\n // standard Material serialization\n data.uuid = this.uuid;\n data.type = this.type;\n\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.color && this.color.isColor ) data.color = this.color.getHex();\n\n if ( this.roughness !== undefined ) data.roughness = this.roughness;\n if ( this.metalness !== undefined ) data.metalness = this.metalness;\n\n if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();\n if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;\n\n if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();\n if ( this.shininess !== undefined ) data.shininess = this.shininess;\n if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;\n if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;\n\n if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;\n if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;\n if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;\n if ( this.bumpMap && this.bumpMap.isTexture ) {\n\n data.bumpMap = this.bumpMap.toJSON( meta ).uuid;\n data.bumpScale = this.bumpScale;\n\n }\n if ( this.normalMap && this.normalMap.isTexture ) {\n\n data.normalMap = this.normalMap.toJSON( meta ).uuid;\n data.normalScale = this.normalScale.toArray();\n\n }\n if ( this.displacementMap && this.displacementMap.isTexture ) {\n\n data.displacementMap = this.displacementMap.toJSON( meta ).uuid;\n data.displacementScale = this.displacementScale;\n data.displacementBias = this.displacementBias;\n\n }\n if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;\n if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;\n\n if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;\n if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;\n\n if ( this.envMap && this.envMap.isTexture ) {\n\n data.envMap = this.envMap.toJSON( meta ).uuid;\n data.reflectivity = this.reflectivity; // Scale behind envMap\n\n }\n\n if ( this.gradientMap && this.gradientMap.isTexture ) {\n\n data.gradientMap = this.gradientMap.toJSON( meta ).uuid;\n\n }\n\n if ( this.size !== undefined ) data.size = this.size;\n if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;\n\n if ( this.blending !== NormalBlending ) data.blending = this.blending;\n if ( this.flatShading === true ) data.flatShading = this.flatShading;\n if ( this.side !== FrontSide ) data.side = this.side;\n if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;\n\n if ( this.opacity < 1 ) data.opacity = this.opacity;\n if ( this.transparent === true ) data.transparent = this.transparent;\n\n data.depthFunc = this.depthFunc;\n data.depthTest = this.depthTest;\n data.depthWrite = this.depthWrite;\n\n // rotation (SpriteMaterial)\n if ( this.rotation !== 0 ) data.rotation = this.rotation;\n\n if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;\n if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;\n if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;\n if ( this.scale !== undefined ) data.scale = this.scale;\n\n if ( this.dithering === true ) data.dithering = true;\n\n if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;\n if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;\n\n if ( this.wireframe === true ) data.wireframe = this.wireframe;\n if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;\n if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;\n if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;\n\n if ( this.morphTargets === true ) data.morphTargets = true;\n if ( this.skinning === true ) data.skinning = true;\n\n if ( this.visible === false ) data.visible = false;\n if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;\n\n // TODO: Copied from Object3D.toJSON\n\n function extractFromCache( cache ) {\n\n var values = [];\n\n for ( var key in cache ) {\n\n var data = cache[ key ];\n delete data.metadata;\n values.push( data );\n\n }\n\n return values;\n\n }\n\n if ( isRoot ) {\n\n var textures = extractFromCache( meta.textures );\n var images = extractFromCache( meta.images );\n\n if ( textures.length > 0 ) data.textures = textures;\n if ( images.length > 0 ) data.images = images;\n\n }\n\n return data;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.name = source.name;\n\n this.fog = source.fog;\n this.lights = source.lights;\n\n this.blending = source.blending;\n this.side = source.side;\n this.flatShading = source.flatShading;\n this.vertexColors = source.vertexColors;\n\n this.opacity = source.opacity;\n this.transparent = source.transparent;\n\n this.blendSrc = source.blendSrc;\n this.blendDst = source.blendDst;\n this.blendEquation = source.blendEquation;\n this.blendSrcAlpha = source.blendSrcAlpha;\n this.blendDstAlpha = source.blendDstAlpha;\n this.blendEquationAlpha = source.blendEquationAlpha;\n\n this.depthFunc = source.depthFunc;\n this.depthTest = source.depthTest;\n this.depthWrite = source.depthWrite;\n\n this.colorWrite = source.colorWrite;\n\n this.precision = source.precision;\n\n this.polygonOffset = source.polygonOffset;\n this.polygonOffsetFactor = source.polygonOffsetFactor;\n this.polygonOffsetUnits = source.polygonOffsetUnits;\n\n this.dithering = source.dithering;\n\n this.alphaTest = source.alphaTest;\n this.premultipliedAlpha = source.premultipliedAlpha;\n\n this.overdraw = source.overdraw;\n\n this.visible = source.visible;\n this.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n this.clipShadows = source.clipShadows;\n this.clipIntersection = source.clipIntersection;\n\n var srcPlanes = source.clippingPlanes,\n dstPlanes = null;\n\n if ( srcPlanes !== null ) {\n\n var n = srcPlanes.length;\n dstPlanes = new Array( n );\n\n for ( var i = 0; i !== n; ++ i )\n dstPlanes[ i ] = srcPlanes[ i ].clone();\n\n }\n\n this.clippingPlanes = dstPlanes;\n\n this.shadowSide = source.shadowSide;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * depthTest: <bool>,\n * depthWrite: <bool>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>\n * }\n */\n\n function MeshBasicMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshBasicMaterial';\n\n this.color = new Color( 0xffffff ); // emissive\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshBasicMaterial.prototype = Object.create( Material.prototype );\n MeshBasicMaterial.prototype.constructor = MeshBasicMaterial;\n\n MeshBasicMaterial.prototype.isMeshBasicMaterial = true;\n\n MeshBasicMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * defines: { \"label\" : \"value\" },\n * uniforms: { \"parameter1\": { value: 1.0 }, \"parameter2\": { value2: 2 } },\n *\n * fragmentShader: <string>,\n * vertexShader: <string>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * lights: <bool>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function ShaderMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'ShaderMaterial';\n\n this.defines = {};\n this.uniforms = {};\n\n this.vertexShader = 'void main() {\\n\\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\\n}';\n this.fragmentShader = 'void main() {\\n\\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\\n}';\n\n this.linewidth = 1;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false; // set to use scene fog\n this.lights = false; // set to use scene lights\n this.clipping = false; // set to use user-defined clipping planes\n\n this.skinning = false; // set to use skinning attribute streams\n this.morphTargets = false; // set to use morph targets\n this.morphNormals = false; // set to use morph normals\n\n this.extensions = {\n derivatives: false, // set to use derivatives\n fragDepth: false, // set to use fragment depth values\n drawBuffers: false, // set to use draw buffers\n shaderTextureLOD: false // set to use shader texture LOD\n };\n\n // When rendered geometry doesn't include these attributes but the material does,\n // use these default values in WebGL. This avoids errors when buffer data is missing.\n this.defaultAttributeValues = {\n 'color': [ 1, 1, 1 ],\n 'uv': [ 0, 0 ],\n 'uv2': [ 0, 0 ]\n };\n\n this.index0AttributeName = undefined;\n this.uniformsNeedUpdate = false;\n\n if ( parameters !== undefined ) {\n\n if ( parameters.attributes !== undefined ) {\n\n console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );\n\n }\n\n this.setValues( parameters );\n\n }\n\n }\n\n ShaderMaterial.prototype = Object.create( Material.prototype );\n ShaderMaterial.prototype.constructor = ShaderMaterial;\n\n ShaderMaterial.prototype.isShaderMaterial = true;\n\n ShaderMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.fragmentShader = source.fragmentShader;\n this.vertexShader = source.vertexShader;\n\n this.uniforms = UniformsUtils.clone( source.uniforms );\n\n this.defines = source.defines;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n this.lights = source.lights;\n this.clipping = source.clipping;\n\n this.skinning = source.skinning;\n\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n this.extensions = source.extensions;\n\n return this;\n\n };\n\n ShaderMaterial.prototype.toJSON = function ( meta ) {\n\n var data = Material.prototype.toJSON.call( this, meta );\n\n data.uniforms = this.uniforms;\n data.vertexShader = this.vertexShader;\n data.fragmentShader = this.fragmentShader;\n\n return data;\n\n };\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Ray( origin, direction ) {\n\n this.origin = ( origin !== undefined ) ? origin : new Vector3();\n this.direction = ( direction !== undefined ) ? direction : new Vector3();\n\n }\n\n Object.assign( Ray.prototype, {\n\n set: function ( origin, direction ) {\n\n this.origin.copy( origin );\n this.direction.copy( direction );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( ray ) {\n\n this.origin.copy( ray.origin );\n this.direction.copy( ray.direction );\n\n return this;\n\n },\n\n at: function ( t, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Ray: .at() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );\n\n },\n\n lookAt: function ( v ) {\n\n this.direction.copy( v ).sub( this.origin ).normalize();\n\n return this;\n\n },\n\n recast: function () {\n\n var v1 = new Vector3();\n\n return function recast( t ) {\n\n this.origin.copy( this.at( t, v1 ) );\n\n return this;\n\n };\n\n }(),\n\n closestPointToPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n target.subVectors( point, this.origin );\n\n var directionDistance = target.dot( this.direction );\n\n if ( directionDistance < 0 ) {\n\n return target.copy( this.origin );\n\n }\n\n return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n },\n\n distanceToPoint: function ( point ) {\n\n return Math.sqrt( this.distanceSqToPoint( point ) );\n\n },\n\n distanceSqToPoint: function () {\n\n var v1 = new Vector3();\n\n return function distanceSqToPoint( point ) {\n\n var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );\n\n // point behind the ray\n\n if ( directionDistance < 0 ) {\n\n return this.origin.distanceToSquared( point );\n\n }\n\n v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n return v1.distanceToSquared( point );\n\n };\n\n }(),\n\n distanceSqToSegment: function () {\n\n var segCenter = new Vector3();\n var segDir = new Vector3();\n var diff = new Vector3();\n\n return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {\n\n // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h\n // It returns the min distance between the ray and the segment\n // defined by v0 and v1\n // It can also set two optional targets :\n // - The closest point on the ray\n // - The closest point on the segment\n\n segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );\n segDir.copy( v1 ).sub( v0 ).normalize();\n diff.copy( this.origin ).sub( segCenter );\n\n var segExtent = v0.distanceTo( v1 ) * 0.5;\n var a01 = - this.direction.dot( segDir );\n var b0 = diff.dot( this.direction );\n var b1 = - diff.dot( segDir );\n var c = diff.lengthSq();\n var det = Math.abs( 1 - a01 * a01 );\n var s0, s1, sqrDist, extDet;\n\n if ( det > 0 ) {\n\n // The ray and segment are not parallel.\n\n s0 = a01 * b1 - b0;\n s1 = a01 * b0 - b1;\n extDet = segExtent * det;\n\n if ( s0 >= 0 ) {\n\n if ( s1 >= - extDet ) {\n\n if ( s1 <= extDet ) {\n\n // region 0\n // Minimum at interior points of ray and segment.\n\n var invDet = 1 / det;\n s0 *= invDet;\n s1 *= invDet;\n sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;\n\n } else {\n\n // region 1\n\n s1 = segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n } else {\n\n // region 5\n\n s1 = - segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n } else {\n\n if ( s1 <= - extDet ) {\n\n // region 4\n\n s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );\n s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n } else if ( s1 <= extDet ) {\n\n // region 3\n\n s0 = 0;\n s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = s1 * ( s1 + 2 * b1 ) + c;\n\n } else {\n\n // region 2\n\n s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );\n s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n }\n\n } else {\n\n // Ray and segment are parallel.\n\n s1 = ( a01 > 0 ) ? - segExtent : segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n if ( optionalPointOnRay ) {\n\n optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );\n\n }\n\n if ( optionalPointOnSegment ) {\n\n optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );\n\n }\n\n return sqrDist;\n\n };\n\n }(),\n\n intersectSphere: function () {\n\n var v1 = new Vector3();\n\n return function intersectSphere( sphere, target ) {\n\n v1.subVectors( sphere.center, this.origin );\n var tca = v1.dot( this.direction );\n var d2 = v1.dot( v1 ) - tca * tca;\n var radius2 = sphere.radius * sphere.radius;\n\n if ( d2 > radius2 ) return null;\n\n var thc = Math.sqrt( radius2 - d2 );\n\n // t0 = first intersect point - entrance on front of sphere\n var t0 = tca - thc;\n\n // t1 = second intersect point - exit point on back of sphere\n var t1 = tca + thc;\n\n // test to see if both t0 and t1 are behind the ray - if so, return null\n if ( t0 < 0 && t1 < 0 ) return null;\n\n // test to see if t0 is behind the ray:\n // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,\n // in order to always return an intersect point that is in front of the ray.\n if ( t0 < 0 ) return this.at( t1, target );\n\n // else t0 is in front of the ray, so return the first collision point scaled by t0\n return this.at( t0, target );\n\n };\n\n }(),\n\n intersectsSphere: function ( sphere ) {\n\n return this.distanceToPoint( sphere.center ) <= sphere.radius;\n\n },\n\n distanceToPlane: function ( plane ) {\n\n var denominator = plane.normal.dot( this.direction );\n\n if ( denominator === 0 ) {\n\n // line is coplanar, return origin\n if ( plane.distanceToPoint( this.origin ) === 0 ) {\n\n return 0;\n\n }\n\n // Null is preferable to undefined since undefined means.... it is undefined\n\n return null;\n\n }\n\n var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;\n\n // Return if the ray never intersects the plane\n\n return t >= 0 ? t : null;\n\n },\n\n intersectPlane: function ( plane, target ) {\n\n var t = this.distanceToPlane( plane );\n\n if ( t === null ) {\n\n return null;\n\n }\n\n return this.at( t, target );\n\n },\n\n intersectsPlane: function ( plane ) {\n\n // check if the ray lies on the plane first\n\n var distToPoint = plane.distanceToPoint( this.origin );\n\n if ( distToPoint === 0 ) {\n\n return true;\n\n }\n\n var denominator = plane.normal.dot( this.direction );\n\n if ( denominator * distToPoint < 0 ) {\n\n return true;\n\n }\n\n // ray origin is behind the plane (and is pointing behind it)\n\n return false;\n\n },\n\n intersectBox: function ( box, target ) {\n\n var tmin, tmax, tymin, tymax, tzmin, tzmax;\n\n var invdirx = 1 / this.direction.x,\n invdiry = 1 / this.direction.y,\n invdirz = 1 / this.direction.z;\n\n var origin = this.origin;\n\n if ( invdirx >= 0 ) {\n\n tmin = ( box.min.x - origin.x ) * invdirx;\n tmax = ( box.max.x - origin.x ) * invdirx;\n\n } else {\n\n tmin = ( box.max.x - origin.x ) * invdirx;\n tmax = ( box.min.x - origin.x ) * invdirx;\n\n }\n\n if ( invdiry >= 0 ) {\n\n tymin = ( box.min.y - origin.y ) * invdiry;\n tymax = ( box.max.y - origin.y ) * invdiry;\n\n } else {\n\n tymin = ( box.max.y - origin.y ) * invdiry;\n tymax = ( box.min.y - origin.y ) * invdiry;\n\n }\n\n if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;\n\n // These lines also handle the case where tmin or tmax is NaN\n // (result of 0 * Infinity). x !== x returns true if x is NaN\n\n if ( tymin > tmin || tmin !== tmin ) tmin = tymin;\n\n if ( tymax < tmax || tmax !== tmax ) tmax = tymax;\n\n if ( invdirz >= 0 ) {\n\n tzmin = ( box.min.z - origin.z ) * invdirz;\n tzmax = ( box.max.z - origin.z ) * invdirz;\n\n } else {\n\n tzmin = ( box.max.z - origin.z ) * invdirz;\n tzmax = ( box.min.z - origin.z ) * invdirz;\n\n }\n\n if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;\n\n if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;\n\n if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;\n\n //return point closest to the ray (positive side)\n\n if ( tmax < 0 ) return null;\n\n return this.at( tmin >= 0 ? tmin : tmax, target );\n\n },\n\n intersectsBox: ( function () {\n\n var v = new Vector3();\n\n return function intersectsBox( box ) {\n\n return this.intersectBox( box, v ) !== null;\n\n };\n\n } )(),\n\n intersectTriangle: function () {\n\n // Compute the offset origin, edges, and normal.\n var diff = new Vector3();\n var edge1 = new Vector3();\n var edge2 = new Vector3();\n var normal = new Vector3();\n\n return function intersectTriangle( a, b, c, backfaceCulling, target ) {\n\n // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h\n\n edge1.subVectors( b, a );\n edge2.subVectors( c, a );\n normal.crossVectors( edge1, edge2 );\n\n // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,\n // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by\n // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))\n // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))\n // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)\n var DdN = this.direction.dot( normal );\n var sign;\n\n if ( DdN > 0 ) {\n\n if ( backfaceCulling ) return null;\n sign = 1;\n\n } else if ( DdN < 0 ) {\n\n sign = - 1;\n DdN = - DdN;\n\n } else {\n\n return null;\n\n }\n\n diff.subVectors( this.origin, a );\n var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );\n\n // b1 < 0, no intersection\n if ( DdQxE2 < 0 ) {\n\n return null;\n\n }\n\n var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );\n\n // b2 < 0, no intersection\n if ( DdE1xQ < 0 ) {\n\n return null;\n\n }\n\n // b1+b2 > 1, no intersection\n if ( DdQxE2 + DdE1xQ > DdN ) {\n\n return null;\n\n }\n\n // Line intersects triangle, check if ray does.\n var QdN = - sign * diff.dot( normal );\n\n // t < 0, no intersection\n if ( QdN < 0 ) {\n\n return null;\n\n }\n\n // Ray intersects triangle.\n return this.at( QdN / DdN, target );\n\n };\n\n }(),\n\n applyMatrix4: function ( matrix4 ) {\n\n this.origin.applyMatrix4( matrix4 );\n this.direction.transformDirection( matrix4 );\n\n return this;\n\n },\n\n equals: function ( ray ) {\n\n return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Line3( start, end ) {\n\n this.start = ( start !== undefined ) ? start : new Vector3();\n this.end = ( end !== undefined ) ? end : new Vector3();\n\n }\n\n Object.assign( Line3.prototype, {\n\n set: function ( start, end ) {\n\n this.start.copy( start );\n this.end.copy( end );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( line ) {\n\n this.start.copy( line.start );\n this.end.copy( line.end );\n\n return this;\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .getCenter() target is now required' );\n target = new Vector3();\n\n }\n\n return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 );\n\n },\n\n delta: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .delta() target is now required' );\n target = new Vector3();\n\n }\n\n return target.subVectors( this.end, this.start );\n\n },\n\n distanceSq: function () {\n\n return this.start.distanceToSquared( this.end );\n\n },\n\n distance: function () {\n\n return this.start.distanceTo( this.end );\n\n },\n\n at: function ( t, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .at() target is now required' );\n target = new Vector3();\n\n }\n\n return this.delta( target ).multiplyScalar( t ).add( this.start );\n\n },\n\n closestPointToPointParameter: function () {\n\n var startP = new Vector3();\n var startEnd = new Vector3();\n\n return function closestPointToPointParameter( point, clampToLine ) {\n\n startP.subVectors( point, this.start );\n startEnd.subVectors( this.end, this.start );\n\n var startEnd2 = startEnd.dot( startEnd );\n var startEnd_startP = startEnd.dot( startP );\n\n var t = startEnd_startP / startEnd2;\n\n if ( clampToLine ) {\n\n t = _Math.clamp( t, 0, 1 );\n\n }\n\n return t;\n\n };\n\n }(),\n\n closestPointToPoint: function ( point, clampToLine, target ) {\n\n var t = this.closestPointToPointParameter( point, clampToLine );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return this.delta( target ).multiplyScalar( t ).add( this.start );\n\n },\n\n applyMatrix4: function ( matrix ) {\n\n this.start.applyMatrix4( matrix );\n this.end.applyMatrix4( matrix );\n\n return this;\n\n },\n\n equals: function ( line ) {\n\n return line.start.equals( this.start ) && line.end.equals( this.end );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Triangle( a, b, c ) {\n\n this.a = ( a !== undefined ) ? a : new Vector3();\n this.b = ( b !== undefined ) ? b : new Vector3();\n this.c = ( c !== undefined ) ? c : new Vector3();\n\n }\n\n Object.assign( Triangle, {\n\n getNormal: function () {\n\n var v0 = new Vector3();\n\n return function getNormal( a, b, c, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getNormal() target is now required' );\n target = new Vector3();\n\n }\n\n target.subVectors( c, b );\n v0.subVectors( a, b );\n target.cross( v0 );\n\n var targetLengthSq = target.lengthSq();\n if ( targetLengthSq > 0 ) {\n\n return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );\n\n }\n\n return target.set( 0, 0, 0 );\n\n };\n\n }(),\n\n // static/instance method to calculate barycentric coordinates\n // based on: http://www.blackpawn.com/texts/pointinpoly/default.html\n getBarycoord: function () {\n\n var v0 = new Vector3();\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function getBarycoord( point, a, b, c, target ) {\n\n v0.subVectors( c, a );\n v1.subVectors( b, a );\n v2.subVectors( point, a );\n\n var dot00 = v0.dot( v0 );\n var dot01 = v0.dot( v1 );\n var dot02 = v0.dot( v2 );\n var dot11 = v1.dot( v1 );\n var dot12 = v1.dot( v2 );\n\n var denom = ( dot00 * dot11 - dot01 * dot01 );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getBarycoord() target is now required' );\n target = new Vector3();\n\n }\n\n // collinear or singular triangle\n if ( denom === 0 ) {\n\n // arbitrary location outside of triangle?\n // not sure if this is the best idea, maybe should be returning undefined\n return target.set( - 2, - 1, - 1 );\n\n }\n\n var invDenom = 1 / denom;\n var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;\n var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;\n\n // barycentric coordinates must always sum to 1\n return target.set( 1 - u - v, v, u );\n\n };\n\n }(),\n\n containsPoint: function () {\n\n var v1 = new Vector3();\n\n return function containsPoint( point, a, b, c ) {\n\n Triangle.getBarycoord( point, a, b, c, v1 );\n\n return ( v1.x >= 0 ) && ( v1.y >= 0 ) && ( ( v1.x + v1.y ) <= 1 );\n\n };\n\n }()\n\n } );\n\n Object.assign( Triangle.prototype, {\n\n set: function ( a, b, c ) {\n\n this.a.copy( a );\n this.b.copy( b );\n this.c.copy( c );\n\n return this;\n\n },\n\n setFromPointsAndIndices: function ( points, i0, i1, i2 ) {\n\n this.a.copy( points[ i0 ] );\n this.b.copy( points[ i1 ] );\n this.c.copy( points[ i2 ] );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( triangle ) {\n\n this.a.copy( triangle.a );\n this.b.copy( triangle.b );\n this.c.copy( triangle.c );\n\n return this;\n\n },\n\n getArea: function () {\n\n var v0 = new Vector3();\n var v1 = new Vector3();\n\n return function getArea() {\n\n v0.subVectors( this.c, this.b );\n v1.subVectors( this.a, this.b );\n\n return v0.cross( v1 ).length() * 0.5;\n\n };\n\n }(),\n\n getMidpoint: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getMidpoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );\n\n },\n\n getNormal: function ( target ) {\n\n return Triangle.getNormal( this.a, this.b, this.c, target );\n\n },\n\n getPlane: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getPlane() target is now required' );\n target = new Vector3();\n\n }\n\n return target.setFromCoplanarPoints( this.a, this.b, this.c );\n\n },\n\n getBarycoord: function ( point, target ) {\n\n return Triangle.getBarycoord( point, this.a, this.b, this.c, target );\n\n },\n\n containsPoint: function ( point ) {\n\n return Triangle.containsPoint( point, this.a, this.b, this.c );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsTriangle( this );\n\n },\n\n closestPointToPoint: function () {\n\n var plane = new Plane();\n var edgeList = [ new Line3(), new Line3(), new Line3() ];\n var projectedPoint = new Vector3();\n var closestPoint = new Vector3();\n\n return function closestPointToPoint( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n var minDistance = Infinity;\n\n // project the point onto the plane of the triangle\n\n plane.setFromCoplanarPoints( this.a, this.b, this.c );\n plane.projectPoint( point, projectedPoint );\n\n // check if the projection lies within the triangle\n\n if ( this.containsPoint( projectedPoint ) === true ) {\n\n // if so, this is the closest point\n\n target.copy( projectedPoint );\n\n } else {\n\n // if not, the point falls outside the triangle. the target is the closest point to the triangle's edges or vertices\n\n edgeList[ 0 ].set( this.a, this.b );\n edgeList[ 1 ].set( this.b, this.c );\n edgeList[ 2 ].set( this.c, this.a );\n\n for ( var i = 0; i < edgeList.length; i ++ ) {\n\n edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint );\n\n var distance = projectedPoint.distanceToSquared( closestPoint );\n\n if ( distance < minDistance ) {\n\n minDistance = distance;\n\n target.copy( closestPoint );\n\n }\n\n }\n\n }\n\n return target;\n\n };\n\n }(),\n\n equals: function ( triangle ) {\n\n return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author jonobr1 / http://jonobr1.com/\n */\n\n function Mesh( geometry, material ) {\n\n Object3D.call( this );\n\n this.type = 'Mesh';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } );\n\n this.drawMode = TrianglesDrawMode;\n\n this.updateMorphTargets();\n\n }\n\n Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Mesh,\n\n isMesh: true,\n\n setDrawMode: function ( value ) {\n\n this.drawMode = value;\n\n },\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n this.drawMode = source.drawMode;\n\n if ( source.morphTargetInfluences !== undefined ) {\n\n this.morphTargetInfluences = source.morphTargetInfluences.slice();\n\n }\n\n if ( source.morphTargetDictionary !== undefined ) {\n\n this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );\n\n }\n\n return this;\n\n },\n\n updateMorphTargets: function () {\n\n var geometry = this.geometry;\n var m, ml, name;\n\n if ( geometry.isBufferGeometry ) {\n\n var morphAttributes = geometry.morphAttributes;\n var keys = Object.keys( morphAttributes );\n\n if ( keys.length > 0 ) {\n\n var morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n if ( morphAttribute !== undefined ) {\n\n this.morphTargetInfluences = [];\n this.morphTargetDictionary = {};\n\n for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n name = morphAttribute[ m ].name || String( m );\n\n this.morphTargetInfluences.push( 0 );\n this.morphTargetDictionary[ name ] = m;\n\n }\n\n }\n\n }\n\n } else {\n\n var morphTargets = geometry.morphTargets;\n\n if ( morphTargets !== undefined && morphTargets.length > 0 ) {\n\n this.morphTargetInfluences = [];\n this.morphTargetDictionary = {};\n\n for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) {\n\n name = morphTargets[ m ].name || String( m );\n\n this.morphTargetInfluences.push( 0 );\n this.morphTargetDictionary[ name ] = m;\n\n }\n\n }\n\n }\n\n },\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n var vA = new Vector3();\n var vB = new Vector3();\n var vC = new Vector3();\n\n var tempA = new Vector3();\n var tempB = new Vector3();\n var tempC = new Vector3();\n\n var uvA = new Vector2();\n var uvB = new Vector2();\n var uvC = new Vector2();\n\n var barycoord = new Vector3();\n\n var intersectionPoint = new Vector3();\n var intersectionPointWorld = new Vector3();\n\n function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {\n\n Triangle.getBarycoord( point, p1, p2, p3, barycoord );\n\n uv1.multiplyScalar( barycoord.x );\n uv2.multiplyScalar( barycoord.y );\n uv3.multiplyScalar( barycoord.z );\n\n uv1.add( uv2 ).add( uv3 );\n\n return uv1.clone();\n\n }\n\n function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {\n\n var intersect;\n\n if ( material.side === BackSide ) {\n\n intersect = ray.intersectTriangle( pC, pB, pA, true, point );\n\n } else {\n\n intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );\n\n }\n\n if ( intersect === null ) return null;\n\n intersectionPointWorld.copy( point );\n intersectionPointWorld.applyMatrix4( object.matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return null;\n\n return {\n distance: distance,\n point: intersectionPointWorld.clone(),\n object: object\n };\n\n }\n\n function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) {\n\n vA.fromBufferAttribute( position, a );\n vB.fromBufferAttribute( position, b );\n vC.fromBufferAttribute( position, c );\n\n var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint );\n\n if ( intersection ) {\n\n if ( uv ) {\n\n uvA.fromBufferAttribute( uv, a );\n uvB.fromBufferAttribute( uv, b );\n uvC.fromBufferAttribute( uv, c );\n\n intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );\n\n }\n\n var face = new Face3( a, b, c );\n Triangle.getNormal( vA, vB, vC, face.normal );\n\n intersection.face = face;\n intersection.faceIndex = a;\n\n }\n\n return intersection;\n\n }\n\n return function raycast( raycaster, intersects ) {\n\n var geometry = this.geometry;\n var material = this.material;\n var matrixWorld = this.matrixWorld;\n\n if ( material === undefined ) return;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n // Check boundingBox before continuing\n\n if ( geometry.boundingBox !== null ) {\n\n if ( ray.intersectsBox( geometry.boundingBox ) === false ) return;\n\n }\n\n var intersection;\n\n if ( geometry.isBufferGeometry ) {\n\n var a, b, c;\n var index = geometry.index;\n var position = geometry.attributes.position;\n var uv = geometry.attributes.uv;\n var i, l;\n\n if ( index !== null ) {\n\n // indexed buffer geometry\n\n for ( i = 0, l = index.count; i < l; i += 3 ) {\n\n a = index.getX( i );\n b = index.getX( i + 1 );\n c = index.getX( i + 2 );\n\n intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );\n\n if ( intersection ) {\n\n intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics\n intersects.push( intersection );\n\n }\n\n }\n\n } else if ( position !== undefined ) {\n\n // non-indexed buffer geometry\n\n for ( i = 0, l = position.count; i < l; i += 3 ) {\n\n a = i;\n b = i + 1;\n c = i + 2;\n\n intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );\n\n if ( intersection ) {\n\n intersection.index = a; // triangle number in positions buffer semantics\n intersects.push( intersection );\n\n }\n\n }\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var fvA, fvB, fvC;\n var isMultiMaterial = Array.isArray( material );\n\n var vertices = geometry.vertices;\n var faces = geometry.faces;\n var uvs;\n\n var faceVertexUvs = geometry.faceVertexUvs[ 0 ];\n if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;\n\n for ( var f = 0, fl = faces.length; f < fl; f ++ ) {\n\n var face = faces[ f ];\n var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material;\n\n if ( faceMaterial === undefined ) continue;\n\n fvA = vertices[ face.a ];\n fvB = vertices[ face.b ];\n fvC = vertices[ face.c ];\n\n if ( faceMaterial.morphTargets === true ) {\n\n var morphTargets = geometry.morphTargets;\n var morphInfluences = this.morphTargetInfluences;\n\n vA.set( 0, 0, 0 );\n vB.set( 0, 0, 0 );\n vC.set( 0, 0, 0 );\n\n for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {\n\n var influence = morphInfluences[ t ];\n\n if ( influence === 0 ) continue;\n\n var targets = morphTargets[ t ].vertices;\n\n vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );\n vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );\n vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );\n\n }\n\n vA.add( fvA );\n vB.add( fvB );\n vC.add( fvC );\n\n fvA = vA;\n fvB = vB;\n fvC = vC;\n\n }\n\n intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint );\n\n if ( intersection ) {\n\n if ( uvs && uvs[ f ] ) {\n\n var uvs_f = uvs[ f ];\n uvA.copy( uvs_f[ 0 ] );\n uvB.copy( uvs_f[ 1 ] );\n uvC.copy( uvs_f[ 2 ] );\n\n intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );\n\n }\n\n intersection.face = face;\n intersection.faceIndex = f;\n intersects.push( intersection );\n\n }\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {\n\n var clearColor = new Color( 0x000000 );\n var clearAlpha = 0;\n\n var planeCamera, planeMesh;\n var boxMesh;\n\n function render( renderList, scene, camera, forceClear ) {\n\n var background = scene.background;\n\n if ( background === null ) {\n\n setClear( clearColor, clearAlpha );\n\n } else if ( background && background.isColor ) {\n\n setClear( background, 1 );\n forceClear = true;\n\n }\n\n if ( renderer.autoClear || forceClear ) {\n\n renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );\n\n }\n\n if ( background && background.isCubeTexture ) {\n\n if ( boxMesh === undefined ) {\n\n boxMesh = new Mesh(\n new BoxBufferGeometry( 1, 1, 1 ),\n new ShaderMaterial( {\n uniforms: ShaderLib.cube.uniforms,\n vertexShader: ShaderLib.cube.vertexShader,\n fragmentShader: ShaderLib.cube.fragmentShader,\n side: BackSide,\n depthTest: true,\n depthWrite: false,\n fog: false\n } )\n );\n\n boxMesh.geometry.removeAttribute( 'normal' );\n boxMesh.geometry.removeAttribute( 'uv' );\n\n boxMesh.onBeforeRender = function ( renderer, scene, camera ) {\n\n this.matrixWorld.copyPosition( camera.matrixWorld );\n\n };\n\n geometries.update( boxMesh.geometry );\n\n }\n\n boxMesh.material.uniforms.tCube.value = background;\n\n renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null );\n\n } else if ( background && background.isTexture ) {\n\n if ( planeCamera === undefined ) {\n\n planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );\n\n planeMesh = new Mesh(\n new PlaneBufferGeometry( 2, 2 ),\n new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } )\n );\n\n geometries.update( planeMesh.geometry );\n\n }\n\n planeMesh.material.map = background;\n\n // TODO Push this to renderList\n\n renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null );\n\n }\n\n }\n\n function setClear( color, alpha ) {\n\n state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha );\n\n }\n\n return {\n\n getClearColor: function () {\n\n return clearColor;\n\n },\n setClearColor: function ( color, alpha ) {\n\n clearColor.set( color );\n clearAlpha = alpha !== undefined ? alpha : 1;\n setClear( clearColor, clearAlpha );\n\n },\n getClearAlpha: function () {\n\n return clearAlpha;\n\n },\n setClearAlpha: function ( alpha ) {\n\n clearAlpha = alpha;\n setClear( clearColor, clearAlpha );\n\n },\n render: render\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLBufferRenderer( gl, extensions, info ) {\n\n var mode;\n\n function setMode( value ) {\n\n mode = value;\n\n }\n\n function render( start, count ) {\n\n gl.drawArrays( mode, start, count );\n\n info.update( count, mode );\n\n }\n\n function renderInstances( geometry, start, count ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n if ( extension === null ) {\n\n console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n var position = geometry.attributes.position;\n\n if ( position.isInterleavedBufferAttribute ) {\n\n count = position.data.count;\n\n extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount );\n\n } else {\n\n extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount );\n\n }\n\n info.update( count, mode, geometry.maxInstancedCount );\n\n }\n\n //\n\n this.setMode = setMode;\n this.render = render;\n this.renderInstances = renderInstances;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLCapabilities( gl, extensions, parameters ) {\n\n var maxAnisotropy;\n\n function getMaxAnisotropy() {\n\n if ( maxAnisotropy !== undefined ) return maxAnisotropy;\n\n var extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n if ( extension !== null ) {\n\n maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n\n } else {\n\n maxAnisotropy = 0;\n\n }\n\n return maxAnisotropy;\n\n }\n\n function getMaxPrecision( precision ) {\n\n if ( precision === 'highp' ) {\n\n if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&\n gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {\n\n return 'highp';\n\n }\n\n precision = 'mediump';\n\n }\n\n if ( precision === 'mediump' ) {\n\n if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&\n gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {\n\n return 'mediump';\n\n }\n\n }\n\n return 'lowp';\n\n }\n\n var precision = parameters.precision !== undefined ? parameters.precision : 'highp';\n var maxPrecision = getMaxPrecision( precision );\n\n if ( maxPrecision !== precision ) {\n\n console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );\n precision = maxPrecision;\n\n }\n\n var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;\n\n var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\n var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );\n var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );\n\n var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\n var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );\n var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );\n\n var vertexTextures = maxVertexTextures > 0;\n var floatFragmentTextures = !! extensions.get( 'OES_texture_float' );\n var floatVertexTextures = vertexTextures && floatFragmentTextures;\n\n return {\n\n getMaxAnisotropy: getMaxAnisotropy,\n getMaxPrecision: getMaxPrecision,\n\n precision: precision,\n logarithmicDepthBuffer: logarithmicDepthBuffer,\n\n maxTextures: maxTextures,\n maxVertexTextures: maxVertexTextures,\n maxTextureSize: maxTextureSize,\n maxCubemapSize: maxCubemapSize,\n\n maxAttributes: maxAttributes,\n maxVertexUniforms: maxVertexUniforms,\n maxVaryings: maxVaryings,\n maxFragmentUniforms: maxFragmentUniforms,\n\n vertexTextures: vertexTextures,\n floatFragmentTextures: floatFragmentTextures,\n floatVertexTextures: floatVertexTextures\n\n };\n\n }\n\n /**\n * @author tschw\n */\n\n function WebGLClipping() {\n\n var scope = this,\n\n globalState = null,\n numGlobalPlanes = 0,\n localClippingEnabled = false,\n renderingShadows = false,\n\n plane = new Plane(),\n viewNormalMatrix = new Matrix3(),\n\n uniform = { value: null, needsUpdate: false };\n\n this.uniform = uniform;\n this.numPlanes = 0;\n this.numIntersection = 0;\n\n this.init = function ( planes, enableLocalClipping, camera ) {\n\n var enabled =\n planes.length !== 0 ||\n enableLocalClipping ||\n // enable state of previous frame - the clipping code has to\n // run another frame in order to reset the state:\n numGlobalPlanes !== 0 ||\n localClippingEnabled;\n\n localClippingEnabled = enableLocalClipping;\n\n globalState = projectPlanes( planes, camera, 0 );\n numGlobalPlanes = planes.length;\n\n return enabled;\n\n };\n\n this.beginShadows = function () {\n\n renderingShadows = true;\n projectPlanes( null );\n\n };\n\n this.endShadows = function () {\n\n renderingShadows = false;\n resetGlobalState();\n\n };\n\n this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) {\n\n if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {\n\n // there's no local clipping\n\n if ( renderingShadows ) {\n\n // there's no global clipping\n\n projectPlanes( null );\n\n } else {\n\n resetGlobalState();\n\n }\n\n } else {\n\n var nGlobal = renderingShadows ? 0 : numGlobalPlanes,\n lGlobal = nGlobal * 4,\n\n dstArray = cache.clippingState || null;\n\n uniform.value = dstArray; // ensure unique state\n\n dstArray = projectPlanes( planes, camera, lGlobal, fromCache );\n\n for ( var i = 0; i !== lGlobal; ++ i ) {\n\n dstArray[ i ] = globalState[ i ];\n\n }\n\n cache.clippingState = dstArray;\n this.numIntersection = clipIntersection ? this.numPlanes : 0;\n this.numPlanes += nGlobal;\n\n }\n\n\n };\n\n function resetGlobalState() {\n\n if ( uniform.value !== globalState ) {\n\n uniform.value = globalState;\n uniform.needsUpdate = numGlobalPlanes > 0;\n\n }\n\n scope.numPlanes = numGlobalPlanes;\n scope.numIntersection = 0;\n\n }\n\n function projectPlanes( planes, camera, dstOffset, skipTransform ) {\n\n var nPlanes = planes !== null ? planes.length : 0,\n dstArray = null;\n\n if ( nPlanes !== 0 ) {\n\n dstArray = uniform.value;\n\n if ( skipTransform !== true || dstArray === null ) {\n\n var flatSize = dstOffset + nPlanes * 4,\n viewMatrix = camera.matrixWorldInverse;\n\n viewNormalMatrix.getNormalMatrix( viewMatrix );\n\n if ( dstArray === null || dstArray.length < flatSize ) {\n\n dstArray = new Float32Array( flatSize );\n\n }\n\n for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {\n\n plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );\n\n plane.normal.toArray( dstArray, i4 );\n dstArray[ i4 + 3 ] = plane.constant;\n\n }\n\n }\n\n uniform.value = dstArray;\n uniform.needsUpdate = true;\n\n }\n\n scope.numPlanes = nPlanes;\n\n return dstArray;\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLExtensions( gl ) {\n\n var extensions = {};\n\n return {\n\n get: function ( name ) {\n\n if ( extensions[ name ] !== undefined ) {\n\n return extensions[ name ];\n\n }\n\n var extension;\n\n switch ( name ) {\n\n case 'WEBGL_depth_texture':\n extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );\n break;\n\n case 'EXT_texture_filter_anisotropic':\n extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );\n break;\n\n case 'WEBGL_compressed_texture_s3tc':\n extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );\n break;\n\n case 'WEBGL_compressed_texture_pvrtc':\n extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );\n break;\n\n case 'WEBGL_compressed_texture_etc1':\n extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' );\n break;\n\n default:\n extension = gl.getExtension( name );\n\n }\n\n if ( extension === null ) {\n\n console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );\n\n }\n\n extensions[ name ] = extension;\n\n return extension;\n\n }\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLGeometries( gl, attributes, info ) {\n\n var geometries = {};\n var wireframeAttributes = {};\n\n function onGeometryDispose( event ) {\n\n var geometry = event.target;\n var buffergeometry = geometries[ geometry.id ];\n\n if ( buffergeometry.index !== null ) {\n\n attributes.remove( buffergeometry.index );\n\n }\n\n for ( var name in buffergeometry.attributes ) {\n\n attributes.remove( buffergeometry.attributes[ name ] );\n\n }\n\n geometry.removeEventListener( 'dispose', onGeometryDispose );\n\n delete geometries[ geometry.id ];\n\n // TODO Remove duplicate code\n\n var attribute = wireframeAttributes[ geometry.id ];\n\n if ( attribute ) {\n\n attributes.remove( attribute );\n delete wireframeAttributes[ geometry.id ];\n\n }\n\n attribute = wireframeAttributes[ buffergeometry.id ];\n\n if ( attribute ) {\n\n attributes.remove( attribute );\n delete wireframeAttributes[ buffergeometry.id ];\n\n }\n\n //\n\n info.memory.geometries --;\n\n }\n\n function get( object, geometry ) {\n\n var buffergeometry = geometries[ geometry.id ];\n\n if ( buffergeometry ) return buffergeometry;\n\n geometry.addEventListener( 'dispose', onGeometryDispose );\n\n if ( geometry.isBufferGeometry ) {\n\n buffergeometry = geometry;\n\n } else if ( geometry.isGeometry ) {\n\n if ( geometry._bufferGeometry === undefined ) {\n\n geometry._bufferGeometry = new BufferGeometry().setFromObject( object );\n\n }\n\n buffergeometry = geometry._bufferGeometry;\n\n }\n\n geometries[ geometry.id ] = buffergeometry;\n\n info.memory.geometries ++;\n\n return buffergeometry;\n\n }\n\n function update( geometry ) {\n\n var index = geometry.index;\n var geometryAttributes = geometry.attributes;\n\n if ( index !== null ) {\n\n attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );\n\n }\n\n for ( var name in geometryAttributes ) {\n\n attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER );\n\n }\n\n // morph targets\n\n var morphAttributes = geometry.morphAttributes;\n\n for ( var name in morphAttributes ) {\n\n var array = morphAttributes[ name ];\n\n for ( var i = 0, l = array.length; i < l; i ++ ) {\n\n attributes.update( array[ i ], gl.ARRAY_BUFFER );\n\n }\n\n }\n\n }\n\n function getWireframeAttribute( geometry ) {\n\n var attribute = wireframeAttributes[ geometry.id ];\n\n if ( attribute ) return attribute;\n\n var indices = [];\n\n var geometryIndex = geometry.index;\n var geometryAttributes = geometry.attributes;\n\n // console.time( 'wireframe' );\n\n if ( geometryIndex !== null ) {\n\n var array = geometryIndex.array;\n\n for ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n var a = array[ i + 0 ];\n var b = array[ i + 1 ];\n var c = array[ i + 2 ];\n\n indices.push( a, b, b, c, c, a );\n\n }\n\n } else {\n\n var array = geometryAttributes.position.array;\n\n for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {\n\n var a = i + 0;\n var b = i + 1;\n var c = i + 2;\n\n indices.push( a, b, b, c, c, a );\n\n }\n\n }\n\n // console.timeEnd( 'wireframe' );\n\n attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );\n\n attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER );\n\n wireframeAttributes[ geometry.id ] = attribute;\n\n return attribute;\n\n }\n\n return {\n\n get: get,\n update: update,\n\n getWireframeAttribute: getWireframeAttribute\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLIndexedBufferRenderer( gl, extensions, info ) {\n\n var mode;\n\n function setMode( value ) {\n\n mode = value;\n\n }\n\n var type, bytesPerElement;\n\n function setIndex( value ) {\n\n type = value.type;\n bytesPerElement = value.bytesPerElement;\n\n }\n\n function render( start, count ) {\n\n gl.drawElements( mode, count, type, start * bytesPerElement );\n\n info.update( count, mode );\n\n }\n\n function renderInstances( geometry, start, count ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n if ( extension === null ) {\n\n console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );\n\n info.update( count, mode, geometry.maxInstancedCount );\n\n }\n\n //\n\n this.setMode = setMode;\n this.setIndex = setIndex;\n this.render = render;\n this.renderInstances = renderInstances;\n\n }\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WebGLInfo( gl ) {\n\n var memory = {\n geometries: 0,\n textures: 0\n };\n\n var render = {\n frame: 0,\n calls: 0,\n triangles: 0,\n points: 0,\n lines: 0\n };\n\n function update( count, mode, instanceCount ) {\n\n instanceCount = instanceCount || 1;\n\n render.calls ++;\n\n switch ( mode ) {\n\n case gl.TRIANGLES:\n render.triangles += instanceCount * ( count / 3 );\n break;\n\n case gl.TRIANGLE_STRIP:\n case gl.TRIANGLE_FAN:\n render.triangles += instanceCount * ( count - 2 );\n break;\n\n case gl.LINES:\n render.lines += instanceCount * ( count / 2 );\n break;\n\n case gl.LINE_STRIP:\n render.lines += instanceCount * ( count - 1 );\n break;\n\n case gl.LINE_LOOP:\n render.lines += instanceCount * count;\n break;\n\n case gl.POINTS:\n render.points += instanceCount * count;\n break;\n\n default:\n console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );\n break;\n\n }\n\n }\n\n function reset() {\n\n render.frame ++;\n render.calls = 0;\n render.triangles = 0;\n render.points = 0;\n render.lines = 0;\n\n }\n\n return {\n memory: memory,\n render: render,\n programs: null,\n autoReset: true,\n reset: reset,\n update: update\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function absNumericalSort( a, b ) {\n\n return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );\n\n }\n\n function WebGLMorphtargets( gl ) {\n\n var influencesList = {};\n var morphInfluences = new Float32Array( 8 );\n\n function update( object, geometry, material, program ) {\n\n var objectInfluences = object.morphTargetInfluences;\n\n var length = objectInfluences.length;\n\n var influences = influencesList[ geometry.id ];\n\n if ( influences === undefined ) {\n\n // initialise list\n\n influences = [];\n\n for ( var i = 0; i < length; i ++ ) {\n\n influences[ i ] = [ i, 0 ];\n\n }\n\n influencesList[ geometry.id ] = influences;\n\n }\n\n var morphTargets = material.morphTargets && geometry.morphAttributes.position;\n var morphNormals = material.morphNormals && geometry.morphAttributes.normal;\n\n // Remove current morphAttributes\n\n for ( var i = 0; i < length; i ++ ) {\n\n var influence = influences[ i ];\n\n if ( influence[ 1 ] !== 0 ) {\n\n if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );\n if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );\n\n }\n\n }\n\n // Collect influences\n\n for ( var i = 0; i < length; i ++ ) {\n\n var influence = influences[ i ];\n\n influence[ 0 ] = i;\n influence[ 1 ] = objectInfluences[ i ];\n\n }\n\n influences.sort( absNumericalSort );\n\n // Add morphAttributes\n\n for ( var i = 0; i < 8; i ++ ) {\n\n var influence = influences[ i ];\n\n if ( influence ) {\n\n var index = influence[ 0 ];\n var value = influence[ 1 ];\n\n if ( value ) {\n\n if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );\n if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );\n\n morphInfluences[ i ] = value;\n continue;\n\n }\n\n }\n\n morphInfluences[ i ] = 0;\n\n }\n\n program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );\n\n }\n\n return {\n\n update: update\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLObjects( geometries, info ) {\n\n var updateList = {};\n\n function update( object ) {\n\n var frame = info.render.frame;\n\n var geometry = object.geometry;\n var buffergeometry = geometries.get( object, geometry );\n\n // Update once per frame\n\n if ( updateList[ buffergeometry.id ] !== frame ) {\n\n if ( geometry.isGeometry ) {\n\n buffergeometry.updateFromObject( object );\n\n }\n\n geometries.update( buffergeometry );\n\n updateList[ buffergeometry.id ] = frame;\n\n }\n\n return buffergeometry;\n\n }\n\n function dispose() {\n\n updateList = {};\n\n }\n\n return {\n\n update: update,\n dispose: dispose\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n images = images !== undefined ? images : [];\n mapping = mapping !== undefined ? mapping : CubeReflectionMapping;\n\n Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.flipY = false;\n\n }\n\n CubeTexture.prototype = Object.create( Texture.prototype );\n CubeTexture.prototype.constructor = CubeTexture;\n\n CubeTexture.prototype.isCubeTexture = true;\n\n Object.defineProperty( CubeTexture.prototype, 'images', {\n\n get: function () {\n\n return this.image;\n\n },\n\n set: function ( value ) {\n\n this.image = value;\n\n }\n\n } );\n\n /**\n * @author tschw\n *\n * Uniforms of a program.\n * Those form a tree structure with a special top-level container for the root,\n * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.\n *\n *\n * Properties of inner nodes including the top-level container:\n *\n * .seq - array of nested uniforms\n * .map - nested uniforms by name\n *\n *\n * Methods of all nodes except the top-level container:\n *\n * .setValue( gl, value, [renderer] )\n *\n * uploads a uniform value(s)\n * the 'renderer' parameter is needed for sampler uniforms\n *\n *\n * Static methods of the top-level container (renderer factorizations):\n *\n * .upload( gl, seq, values, renderer )\n *\n * sets uniforms in 'seq' to 'values[id].value'\n *\n * .seqWithValue( seq, values ) : filteredSeq\n *\n * filters 'seq' entries with corresponding entry in values\n *\n *\n * Methods of the top-level container (renderer factorizations):\n *\n * .setValue( gl, name, value )\n *\n * sets uniform with name 'name' to 'value'\n *\n * .set( gl, obj, prop )\n *\n * sets uniform from object and property with same name than uniform\n *\n * .setOptional( gl, obj, prop )\n *\n * like .set for an optional property of the object\n *\n */\n\n var emptyTexture = new Texture();\n var emptyCubeTexture = new CubeTexture();\n\n // --- Base for inner nodes (including the root) ---\n\n function UniformContainer() {\n\n this.seq = [];\n this.map = {};\n\n }\n\n // --- Utilities ---\n\n // Array Caches (provide typed arrays for temporary by size)\n\n var arrayCacheF32 = [];\n var arrayCacheI32 = [];\n\n // Float32Array caches used for uploading Matrix uniforms\n\n var mat4array = new Float32Array( 16 );\n var mat3array = new Float32Array( 9 );\n\n // Flattening for arrays of vectors and matrices\n\n function flatten( array, nBlocks, blockSize ) {\n\n var firstElem = array[ 0 ];\n\n if ( firstElem <= 0 || firstElem > 0 ) return array;\n // unoptimized: ! isNaN( firstElem )\n // see http://jacksondunstan.com/articles/983\n\n var n = nBlocks * blockSize,\n r = arrayCacheF32[ n ];\n\n if ( r === undefined ) {\n\n r = new Float32Array( n );\n arrayCacheF32[ n ] = r;\n\n }\n\n if ( nBlocks !== 0 ) {\n\n firstElem.toArray( r, 0 );\n\n for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {\n\n offset += blockSize;\n array[ i ].toArray( r, offset );\n\n }\n\n }\n\n return r;\n\n }\n\n // Texture unit allocation\n\n function allocTexUnits( renderer, n ) {\n\n var r = arrayCacheI32[ n ];\n\n if ( r === undefined ) {\n\n r = new Int32Array( n );\n arrayCacheI32[ n ] = r;\n\n }\n\n for ( var i = 0; i !== n; ++ i )\n r[ i ] = renderer.allocTextureUnit();\n\n return r;\n\n }\n\n // --- Setters ---\n\n // Note: Defining these methods externally, because they come in a bunch\n // and this way their names minify.\n\n // Single scalar\n\n function setValue1f( gl, v ) {\n\n gl.uniform1f( this.addr, v );\n\n }\n\n function setValue1i( gl, v ) {\n\n gl.uniform1i( this.addr, v );\n\n }\n\n // Single float vector (from flat array or THREE.VectorN)\n\n function setValue2fv( gl, v ) {\n\n if ( v.x === undefined ) {\n\n gl.uniform2fv( this.addr, v );\n\n } else {\n\n gl.uniform2f( this.addr, v.x, v.y );\n\n }\n\n }\n\n function setValue3fv( gl, v ) {\n\n if ( v.x !== undefined ) {\n\n gl.uniform3f( this.addr, v.x, v.y, v.z );\n\n } else if ( v.r !== undefined ) {\n\n gl.uniform3f( this.addr, v.r, v.g, v.b );\n\n } else {\n\n gl.uniform3fv( this.addr, v );\n\n }\n\n }\n\n function setValue4fv( gl, v ) {\n\n if ( v.x === undefined ) {\n\n gl.uniform4fv( this.addr, v );\n\n } else {\n\n gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );\n\n }\n\n }\n\n // Single matrix (from flat array or MatrixN)\n\n function setValue2fm( gl, v ) {\n\n gl.uniformMatrix2fv( this.addr, false, v.elements || v );\n\n }\n\n function setValue3fm( gl, v ) {\n\n if ( v.elements === undefined ) {\n\n gl.uniformMatrix3fv( this.addr, false, v );\n\n } else {\n\n mat3array.set( v.elements );\n gl.uniformMatrix3fv( this.addr, false, mat3array );\n\n }\n\n }\n\n function setValue4fm( gl, v ) {\n\n if ( v.elements === undefined ) {\n\n gl.uniformMatrix4fv( this.addr, false, v );\n\n } else {\n\n mat4array.set( v.elements );\n gl.uniformMatrix4fv( this.addr, false, mat4array );\n\n }\n\n }\n\n // Single texture (2D / Cube)\n\n function setValueT1( gl, v, renderer ) {\n\n var unit = renderer.allocTextureUnit();\n gl.uniform1i( this.addr, unit );\n renderer.setTexture2D( v || emptyTexture, unit );\n\n }\n\n function setValueT6( gl, v, renderer ) {\n\n var unit = renderer.allocTextureUnit();\n gl.uniform1i( this.addr, unit );\n renderer.setTextureCube( v || emptyCubeTexture, unit );\n\n }\n\n // Integer / Boolean vectors or arrays thereof (always flat arrays)\n\n function setValue2iv( gl, v ) {\n\n gl.uniform2iv( this.addr, v );\n\n }\n\n function setValue3iv( gl, v ) {\n\n gl.uniform3iv( this.addr, v );\n\n }\n\n function setValue4iv( gl, v ) {\n\n gl.uniform4iv( this.addr, v );\n\n }\n\n // Helper to pick the right setter for the singular case\n\n function getSingularSetter( type ) {\n\n switch ( type ) {\n\n case 0x1406: return setValue1f; // FLOAT\n case 0x8b50: return setValue2fv; // _VEC2\n case 0x8b51: return setValue3fv; // _VEC3\n case 0x8b52: return setValue4fv; // _VEC4\n\n case 0x8b5a: return setValue2fm; // _MAT2\n case 0x8b5b: return setValue3fm; // _MAT3\n case 0x8b5c: return setValue4fm; // _MAT4\n\n case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES\n case 0x8b60: return setValueT6; // SAMPLER_CUBE\n\n case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL\n case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n }\n\n }\n\n // Array of scalars\n\n function setValue1fv( gl, v ) {\n\n gl.uniform1fv( this.addr, v );\n\n }\n function setValue1iv( gl, v ) {\n\n gl.uniform1iv( this.addr, v );\n\n }\n\n // Array of vectors (flat or from THREE classes)\n\n function setValueV2a( gl, v ) {\n\n gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );\n\n }\n\n function setValueV3a( gl, v ) {\n\n gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );\n\n }\n\n function setValueV4a( gl, v ) {\n\n gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );\n\n }\n\n // Array of matrices (flat or from THREE clases)\n\n function setValueM2a( gl, v ) {\n\n gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );\n\n }\n\n function setValueM3a( gl, v ) {\n\n gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );\n\n }\n\n function setValueM4a( gl, v ) {\n\n gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );\n\n }\n\n // Array of textures (2D / Cube)\n\n function setValueT1a( gl, v, renderer ) {\n\n var n = v.length,\n units = allocTexUnits( renderer, n );\n\n gl.uniform1iv( this.addr, units );\n\n for ( var i = 0; i !== n; ++ i ) {\n\n renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );\n\n }\n\n }\n\n function setValueT6a( gl, v, renderer ) {\n\n var n = v.length,\n units = allocTexUnits( renderer, n );\n\n gl.uniform1iv( this.addr, units );\n\n for ( var i = 0; i !== n; ++ i ) {\n\n renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );\n\n }\n\n }\n\n // Helper to pick the right setter for a pure (bottom-level) array\n\n function getPureArraySetter( type ) {\n\n switch ( type ) {\n\n case 0x1406: return setValue1fv; // FLOAT\n case 0x8b50: return setValueV2a; // _VEC2\n case 0x8b51: return setValueV3a; // _VEC3\n case 0x8b52: return setValueV4a; // _VEC4\n\n case 0x8b5a: return setValueM2a; // _MAT2\n case 0x8b5b: return setValueM3a; // _MAT3\n case 0x8b5c: return setValueM4a; // _MAT4\n\n case 0x8b5e: return setValueT1a; // SAMPLER_2D\n case 0x8b60: return setValueT6a; // SAMPLER_CUBE\n\n case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL\n case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n }\n\n }\n\n // --- Uniform Classes ---\n\n function SingleUniform( id, activeInfo, addr ) {\n\n this.id = id;\n this.addr = addr;\n this.setValue = getSingularSetter( activeInfo.type );\n\n // this.path = activeInfo.name; // DEBUG\n\n }\n\n function PureArrayUniform( id, activeInfo, addr ) {\n\n this.id = id;\n this.addr = addr;\n this.size = activeInfo.size;\n this.setValue = getPureArraySetter( activeInfo.type );\n\n // this.path = activeInfo.name; // DEBUG\n\n }\n\n function StructuredUniform( id ) {\n\n this.id = id;\n\n UniformContainer.call( this ); // mix-in\n\n }\n\n StructuredUniform.prototype.setValue = function ( gl, value ) {\n\n // Note: Don't need an extra 'renderer' parameter, since samplers\n // are not allowed in structured uniforms.\n\n var seq = this.seq;\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ];\n u.setValue( gl, value[ u.id ] );\n\n }\n\n };\n\n // --- Top-level ---\n\n // Parser - builds up the property tree from the path strings\n\n var RePathPart = /([\\w\\d_]+)(\\])?(\\[|\\.)?/g;\n\n // extracts\n // - the identifier (member name or array index)\n // - followed by an optional right bracket (found when array index)\n // - followed by an optional left bracket or dot (type of subscript)\n //\n // Note: These portions can be read in a non-overlapping fashion and\n // allow straightforward parsing of the hierarchy that WebGL encodes\n // in the uniform names.\n\n function addUniform( container, uniformObject ) {\n\n container.seq.push( uniformObject );\n container.map[ uniformObject.id ] = uniformObject;\n\n }\n\n function parseUniform( activeInfo, addr, container ) {\n\n var path = activeInfo.name,\n pathLength = path.length;\n\n // reset RegExp object, because of the early exit of a previous run\n RePathPart.lastIndex = 0;\n\n for ( ; ; ) {\n\n var match = RePathPart.exec( path ),\n matchEnd = RePathPart.lastIndex,\n\n id = match[ 1 ],\n idIsIndex = match[ 2 ] === ']',\n subscript = match[ 3 ];\n\n if ( idIsIndex ) id = id | 0; // convert to integer\n\n if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {\n\n // bare name or \"pure\" bottom-level array \"[0]\" suffix\n\n addUniform( container, subscript === undefined ?\n new SingleUniform( id, activeInfo, addr ) :\n new PureArrayUniform( id, activeInfo, addr ) );\n\n break;\n\n } else {\n\n // step into inner node / create it in case it doesn't exist\n\n var map = container.map, next = map[ id ];\n\n if ( next === undefined ) {\n\n next = new StructuredUniform( id );\n addUniform( container, next );\n\n }\n\n container = next;\n\n }\n\n }\n\n }\n\n // Root Container\n\n function WebGLUniforms( gl, program, renderer ) {\n\n UniformContainer.call( this );\n\n this.renderer = renderer;\n\n var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n\n for ( var i = 0; i < n; ++ i ) {\n\n var info = gl.getActiveUniform( program, i ),\n path = info.name,\n addr = gl.getUniformLocation( program, path );\n\n parseUniform( info, addr, this );\n\n }\n\n }\n\n WebGLUniforms.prototype.setValue = function ( gl, name, value ) {\n\n var u = this.map[ name ];\n\n if ( u !== undefined ) u.setValue( gl, value, this.renderer );\n\n };\n\n WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {\n\n var v = object[ name ];\n\n if ( v !== undefined ) this.setValue( gl, name, v );\n\n };\n\n\n // Static interface\n\n WebGLUniforms.upload = function ( gl, seq, values, renderer ) {\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ],\n v = values[ u.id ];\n\n if ( v.needsUpdate !== false ) {\n\n // note: always updating when .needsUpdate is undefined\n u.setValue( gl, v.value, renderer );\n\n }\n\n }\n\n };\n\n WebGLUniforms.seqWithValue = function ( seq, values ) {\n\n var r = [];\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ];\n if ( u.id in values ) r.push( u );\n\n }\n\n return r;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function addLineNumbers( string ) {\n\n var lines = string.split( '\\n' );\n\n for ( var i = 0; i < lines.length; i ++ ) {\n\n lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];\n\n }\n\n return lines.join( '\\n' );\n\n }\n\n function WebGLShader( gl, type, string ) {\n\n var shader = gl.createShader( type );\n\n gl.shaderSource( shader, string );\n gl.compileShader( shader );\n\n if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {\n\n console.error( 'THREE.WebGLShader: Shader couldn\\'t compile.' );\n\n }\n\n if ( gl.getShaderInfoLog( shader ) !== '' ) {\n\n console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );\n\n }\n\n // --enable-privileged-webgl-extension\n // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );\n\n return shader;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var programIdCount = 0;\n\n function getEncodingComponents( encoding ) {\n\n switch ( encoding ) {\n\n case LinearEncoding:\n return [ 'Linear', '( value )' ];\n case sRGBEncoding:\n return [ 'sRGB', '( value )' ];\n case RGBEEncoding:\n return [ 'RGBE', '( value )' ];\n case RGBM7Encoding:\n return [ 'RGBM', '( value, 7.0 )' ];\n case RGBM16Encoding:\n return [ 'RGBM', '( value, 16.0 )' ];\n case RGBDEncoding:\n return [ 'RGBD', '( value, 256.0 )' ];\n case GammaEncoding:\n return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];\n default:\n throw new Error( 'unsupported encoding: ' + encoding );\n\n }\n\n }\n\n function getTexelDecodingFunction( functionName, encoding ) {\n\n var components = getEncodingComponents( encoding );\n return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }';\n\n }\n\n function getTexelEncodingFunction( functionName, encoding ) {\n\n var components = getEncodingComponents( encoding );\n return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }';\n\n }\n\n function getToneMappingFunction( functionName, toneMapping ) {\n\n var toneMappingName;\n\n switch ( toneMapping ) {\n\n case LinearToneMapping:\n toneMappingName = 'Linear';\n break;\n\n case ReinhardToneMapping:\n toneMappingName = 'Reinhard';\n break;\n\n case Uncharted2ToneMapping:\n toneMappingName = 'Uncharted2';\n break;\n\n case CineonToneMapping:\n toneMappingName = 'OptimizedCineon';\n break;\n\n default:\n throw new Error( 'unsupported toneMapping: ' + toneMapping );\n\n }\n\n return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';\n\n }\n\n function generateExtensions( extensions, parameters, rendererExtensions ) {\n\n extensions = extensions || {};\n\n var chunks = [\n ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',\n ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',\n ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',\n ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''\n ];\n\n return chunks.filter( filterEmptyLine ).join( '\\n' );\n\n }\n\n function generateDefines( defines ) {\n\n var chunks = [];\n\n for ( var name in defines ) {\n\n var value = defines[ name ];\n\n if ( value === false ) continue;\n\n chunks.push( '#define ' + name + ' ' + value );\n\n }\n\n return chunks.join( '\\n' );\n\n }\n\n function fetchAttributeLocations( gl, program ) {\n\n var attributes = {};\n\n var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n for ( var i = 0; i < n; i ++ ) {\n\n var info = gl.getActiveAttrib( program, i );\n var name = info.name;\n\n // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );\n\n attributes[ name ] = gl.getAttribLocation( program, name );\n\n }\n\n return attributes;\n\n }\n\n function filterEmptyLine( string ) {\n\n return string !== '';\n\n }\n\n function replaceLightNums( string, parameters ) {\n\n return string\n .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )\n .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )\n .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )\n .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )\n .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights );\n\n }\n\n function replaceClippingPlaneNums( string, parameters ) {\n\n return string\n .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )\n .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );\n\n }\n\n function parseIncludes( string ) {\n\n var pattern = /^[ \\t]*#include +<([\\w\\d.]+)>/gm;\n\n function replace( match, include ) {\n\n var replace = ShaderChunk[ include ];\n\n if ( replace === undefined ) {\n\n throw new Error( 'Can not resolve #include <' + include + '>' );\n\n }\n\n return parseIncludes( replace );\n\n }\n\n return string.replace( pattern, replace );\n\n }\n\n function unrollLoops( string ) {\n\n var pattern = /#pragma unroll_loop[\\s]+?for \\( int i \\= (\\d+)\\; i < (\\d+)\\; i \\+\\+ \\) \\{([\\s\\S]+?)(?=\\})\\}/g;\n\n function replace( match, start, end, snippet ) {\n\n var unroll = '';\n\n for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {\n\n unroll += snippet.replace( /\\[ i \\]/g, '[ ' + i + ' ]' );\n\n }\n\n return unroll;\n\n }\n\n return string.replace( pattern, replace );\n\n }\n\n function WebGLProgram( renderer, extensions, code, material, shader, parameters ) {\n\n var gl = renderer.context;\n\n var defines = material.defines;\n\n var vertexShader = shader.vertexShader;\n var fragmentShader = shader.fragmentShader;\n\n var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';\n\n if ( parameters.shadowMapType === PCFShadowMap ) {\n\n shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';\n\n } else if ( parameters.shadowMapType === PCFSoftShadowMap ) {\n\n shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';\n\n }\n\n var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';\n var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\n if ( parameters.envMap ) {\n\n switch ( material.envMap.mapping ) {\n\n case CubeReflectionMapping:\n case CubeRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n break;\n\n case CubeUVReflectionMapping:\n case CubeUVRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';\n break;\n\n case EquirectangularReflectionMapping:\n case EquirectangularRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';\n break;\n\n case SphericalReflectionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';\n break;\n\n }\n\n switch ( material.envMap.mapping ) {\n\n case CubeRefractionMapping:\n case EquirectangularRefractionMapping:\n envMapModeDefine = 'ENVMAP_MODE_REFRACTION';\n break;\n\n }\n\n switch ( material.combine ) {\n\n case MultiplyOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n break;\n\n case MixOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';\n break;\n\n case AddOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';\n break;\n\n }\n\n }\n\n var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;\n\n // console.log( 'building new program ' );\n\n //\n\n var customExtensions = generateExtensions( material.extensions, parameters, extensions );\n\n var customDefines = generateDefines( defines );\n\n //\n\n var program = gl.createProgram();\n\n var prefixVertex, prefixFragment;\n\n if ( material.isRawShaderMaterial ) {\n\n prefixVertex = [\n\n customDefines\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n if ( prefixVertex.length > 0 ) {\n\n prefixVertex += '\\n';\n\n }\n\n prefixFragment = [\n\n customExtensions,\n customDefines\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n if ( prefixFragment.length > 0 ) {\n\n prefixFragment += '\\n';\n\n }\n\n } else {\n\n prefixVertex = [\n\n 'precision ' + parameters.precision + ' float;',\n 'precision ' + parameters.precision + ' int;',\n\n '#define SHADER_NAME ' + shader.name,\n\n customDefines,\n\n parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',\n\n '#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n '#define MAX_BONES ' + parameters.maxBones,\n ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',\n ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',\n\n parameters.map ? '#define USE_MAP' : '',\n parameters.envMap ? '#define USE_ENVMAP' : '',\n parameters.envMap ? '#define ' + envMapModeDefine : '',\n parameters.lightMap ? '#define USE_LIGHTMAP' : '',\n parameters.aoMap ? '#define USE_AOMAP' : '',\n parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n parameters.bumpMap ? '#define USE_BUMPMAP' : '',\n parameters.normalMap ? '#define USE_NORMALMAP' : '',\n parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',\n parameters.specularMap ? '#define USE_SPECULARMAP' : '',\n parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n parameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n parameters.vertexColors ? '#define USE_COLOR' : '',\n\n parameters.flatShading ? '#define FLAT_SHADED' : '',\n\n parameters.skinning ? '#define USE_SKINNING' : '',\n parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',\n\n parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',\n parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',\n parameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n parameters.flipSided ? '#define FLIP_SIDED' : '',\n\n parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',\n\n parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n 'uniform mat4 modelMatrix;',\n 'uniform mat4 modelViewMatrix;',\n 'uniform mat4 projectionMatrix;',\n 'uniform mat4 viewMatrix;',\n 'uniform mat3 normalMatrix;',\n 'uniform vec3 cameraPosition;',\n\n 'attribute vec3 position;',\n 'attribute vec3 normal;',\n 'attribute vec2 uv;',\n\n '#ifdef USE_COLOR',\n\n ' attribute vec3 color;',\n\n '#endif',\n\n '#ifdef USE_MORPHTARGETS',\n\n ' attribute vec3 morphTarget0;',\n ' attribute vec3 morphTarget1;',\n ' attribute vec3 morphTarget2;',\n ' attribute vec3 morphTarget3;',\n\n ' #ifdef USE_MORPHNORMALS',\n\n ' attribute vec3 morphNormal0;',\n ' attribute vec3 morphNormal1;',\n ' attribute vec3 morphNormal2;',\n ' attribute vec3 morphNormal3;',\n\n ' #else',\n\n ' attribute vec3 morphTarget4;',\n ' attribute vec3 morphTarget5;',\n ' attribute vec3 morphTarget6;',\n ' attribute vec3 morphTarget7;',\n\n ' #endif',\n\n '#endif',\n\n '#ifdef USE_SKINNING',\n\n ' attribute vec4 skinIndex;',\n ' attribute vec4 skinWeight;',\n\n '#endif',\n\n '\\n'\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n prefixFragment = [\n\n customExtensions,\n\n 'precision ' + parameters.precision + ' float;',\n 'precision ' + parameters.precision + ' int;',\n\n '#define SHADER_NAME ' + shader.name,\n\n customDefines,\n\n parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',\n\n '#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',\n ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',\n\n parameters.map ? '#define USE_MAP' : '',\n parameters.envMap ? '#define USE_ENVMAP' : '',\n parameters.envMap ? '#define ' + envMapTypeDefine : '',\n parameters.envMap ? '#define ' + envMapModeDefine : '',\n parameters.envMap ? '#define ' + envMapBlendingDefine : '',\n parameters.lightMap ? '#define USE_LIGHTMAP' : '',\n parameters.aoMap ? '#define USE_AOMAP' : '',\n parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n parameters.bumpMap ? '#define USE_BUMPMAP' : '',\n parameters.normalMap ? '#define USE_NORMALMAP' : '',\n parameters.specularMap ? '#define USE_SPECULARMAP' : '',\n parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n parameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n parameters.vertexColors ? '#define USE_COLOR' : '',\n\n parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',\n\n parameters.flatShading ? '#define FLAT_SHADED' : '',\n\n parameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n parameters.flipSided ? '#define FLIP_SIDED' : '',\n\n parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',\n\n parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '',\n\n parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n parameters.envMap && extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '',\n\n 'uniform mat4 viewMatrix;',\n 'uniform vec3 cameraPosition;',\n\n ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',\n ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below\n ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',\n\n parameters.dithering ? '#define DITHERING' : '',\n\n ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below\n parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '',\n parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',\n parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',\n parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '',\n\n parameters.depthPacking ? '#define DEPTH_PACKING ' + material.depthPacking : '',\n\n '\\n'\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n }\n\n vertexShader = parseIncludes( vertexShader );\n vertexShader = replaceLightNums( vertexShader, parameters );\n vertexShader = replaceClippingPlaneNums( vertexShader, parameters );\n\n fragmentShader = parseIncludes( fragmentShader );\n fragmentShader = replaceLightNums( fragmentShader, parameters );\n fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );\n\n vertexShader = unrollLoops( vertexShader );\n fragmentShader = unrollLoops( fragmentShader );\n\n var vertexGlsl = prefixVertex + vertexShader;\n var fragmentGlsl = prefixFragment + fragmentShader;\n\n // console.log( '*VERTEX*', vertexGlsl );\n // console.log( '*FRAGMENT*', fragmentGlsl );\n\n var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );\n var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );\n\n gl.attachShader( program, glVertexShader );\n gl.attachShader( program, glFragmentShader );\n\n // Force a particular attribute to index 0.\n\n if ( material.index0AttributeName !== undefined ) {\n\n gl.bindAttribLocation( program, 0, material.index0AttributeName );\n\n } else if ( parameters.morphTargets === true ) {\n\n // programs with morphTargets displace position out of attribute 0\n gl.bindAttribLocation( program, 0, 'position' );\n\n }\n\n gl.linkProgram( program );\n\n var programLog = gl.getProgramInfoLog( program ).trim();\n var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();\n var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();\n\n var runnable = true;\n var haveDiagnostics = true;\n\n // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) );\n // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) );\n\n if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {\n\n runnable = false;\n\n console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );\n\n } else if ( programLog !== '' ) {\n\n console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );\n\n } else if ( vertexLog === '' || fragmentLog === '' ) {\n\n haveDiagnostics = false;\n\n }\n\n if ( haveDiagnostics ) {\n\n this.diagnostics = {\n\n runnable: runnable,\n material: material,\n\n programLog: programLog,\n\n vertexShader: {\n\n log: vertexLog,\n prefix: prefixVertex\n\n },\n\n fragmentShader: {\n\n log: fragmentLog,\n prefix: prefixFragment\n\n }\n\n };\n\n }\n\n // clean up\n\n gl.deleteShader( glVertexShader );\n gl.deleteShader( glFragmentShader );\n\n // set up caching for uniform locations\n\n var cachedUniforms;\n\n this.getUniforms = function () {\n\n if ( cachedUniforms === undefined ) {\n\n cachedUniforms = new WebGLUniforms( gl, program, renderer );\n\n }\n\n return cachedUniforms;\n\n };\n\n // set up caching for attribute locations\n\n var cachedAttributes;\n\n this.getAttributes = function () {\n\n if ( cachedAttributes === undefined ) {\n\n cachedAttributes = fetchAttributeLocations( gl, program );\n\n }\n\n return cachedAttributes;\n\n };\n\n // free resource\n\n this.destroy = function () {\n\n gl.deleteProgram( program );\n this.program = undefined;\n\n };\n\n // DEPRECATED\n\n Object.defineProperties( this, {\n\n uniforms: {\n get: function () {\n\n console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );\n return this.getUniforms();\n\n }\n },\n\n attributes: {\n get: function () {\n\n console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );\n return this.getAttributes();\n\n }\n }\n\n } );\n\n\n //\n\n this.id = programIdCount ++;\n this.code = code;\n this.usedTimes = 1;\n this.program = program;\n this.vertexShader = glVertexShader;\n this.fragmentShader = glFragmentShader;\n\n return this;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLPrograms( renderer, extensions, capabilities ) {\n\n var programs = [];\n\n var shaderIDs = {\n MeshDepthMaterial: 'depth',\n MeshDistanceMaterial: 'distanceRGBA',\n MeshNormalMaterial: 'normal',\n MeshBasicMaterial: 'basic',\n MeshLambertMaterial: 'lambert',\n MeshPhongMaterial: 'phong',\n MeshToonMaterial: 'phong',\n MeshStandardMaterial: 'physical',\n MeshPhysicalMaterial: 'physical',\n LineBasicMaterial: 'basic',\n LineDashedMaterial: 'dashed',\n PointsMaterial: 'points',\n ShadowMaterial: 'shadow'\n };\n\n var parameterNames = [\n \"precision\", \"supportsVertexTextures\", \"map\", \"mapEncoding\", \"envMap\", \"envMapMode\", \"envMapEncoding\",\n \"lightMap\", \"aoMap\", \"emissiveMap\", \"emissiveMapEncoding\", \"bumpMap\", \"normalMap\", \"displacementMap\", \"specularMap\",\n \"roughnessMap\", \"metalnessMap\", \"gradientMap\",\n \"alphaMap\", \"combine\", \"vertexColors\", \"fog\", \"useFog\", \"fogExp\",\n \"flatShading\", \"sizeAttenuation\", \"logarithmicDepthBuffer\", \"skinning\",\n \"maxBones\", \"useVertexTexture\", \"morphTargets\", \"morphNormals\",\n \"maxMorphTargets\", \"maxMorphNormals\", \"premultipliedAlpha\",\n \"numDirLights\", \"numPointLights\", \"numSpotLights\", \"numHemiLights\", \"numRectAreaLights\",\n \"shadowMapEnabled\", \"shadowMapType\", \"toneMapping\", 'physicallyCorrectLights',\n \"alphaTest\", \"doubleSided\", \"flipSided\", \"numClippingPlanes\", \"numClipIntersection\", \"depthPacking\", \"dithering\"\n ];\n\n\n function allocateBones( object ) {\n\n var skeleton = object.skeleton;\n var bones = skeleton.bones;\n\n if ( capabilities.floatVertexTextures ) {\n\n return 1024;\n\n } else {\n\n // default for when object is not specified\n // ( for example when prebuilding shader to be used with multiple objects )\n //\n // - leave some extra space for other uniforms\n // - limit here is ANGLE's 254 max uniform vectors\n // (up to 54 should be safe)\n\n var nVertexUniforms = capabilities.maxVertexUniforms;\n var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );\n\n var maxBones = Math.min( nVertexMatrices, bones.length );\n\n if ( maxBones < bones.length ) {\n\n console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' );\n return 0;\n\n }\n\n return maxBones;\n\n }\n\n }\n\n function getTextureEncodingFromMap( map, gammaOverrideLinear ) {\n\n var encoding;\n\n if ( ! map ) {\n\n encoding = LinearEncoding;\n\n } else if ( map.isTexture ) {\n\n encoding = map.encoding;\n\n } else if ( map.isWebGLRenderTarget ) {\n\n console.warn( \"THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead.\" );\n encoding = map.texture.encoding;\n\n }\n\n // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point.\n if ( encoding === LinearEncoding && gammaOverrideLinear ) {\n\n encoding = GammaEncoding;\n\n }\n\n return encoding;\n\n }\n\n this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) {\n\n var shaderID = shaderIDs[ material.type ];\n\n // heuristics to create shader parameters according to lights in the scene\n // (not to blow over maxLights budget)\n\n var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0;\n var precision = capabilities.precision;\n\n if ( material.precision !== null ) {\n\n precision = capabilities.getMaxPrecision( material.precision );\n\n if ( precision !== material.precision ) {\n\n console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );\n\n }\n\n }\n\n var currentRenderTarget = renderer.getRenderTarget();\n\n var parameters = {\n\n shaderID: shaderID,\n\n precision: precision,\n supportsVertexTextures: capabilities.vertexTextures,\n outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ),\n map: !! material.map,\n mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ),\n envMap: !! material.envMap,\n envMapMode: material.envMap && material.envMap.mapping,\n envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ),\n envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ),\n lightMap: !! material.lightMap,\n aoMap: !! material.aoMap,\n emissiveMap: !! material.emissiveMap,\n emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ),\n bumpMap: !! material.bumpMap,\n normalMap: !! material.normalMap,\n displacementMap: !! material.displacementMap,\n roughnessMap: !! material.roughnessMap,\n metalnessMap: !! material.metalnessMap,\n specularMap: !! material.specularMap,\n alphaMap: !! material.alphaMap,\n\n gradientMap: !! material.gradientMap,\n\n combine: material.combine,\n\n vertexColors: material.vertexColors,\n\n fog: !! fog,\n useFog: material.fog,\n fogExp: ( fog && fog.isFogExp2 ),\n\n flatShading: material.flatShading,\n\n sizeAttenuation: material.sizeAttenuation,\n logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,\n\n skinning: material.skinning && maxBones > 0,\n maxBones: maxBones,\n useVertexTexture: capabilities.floatVertexTextures,\n\n morphTargets: material.morphTargets,\n morphNormals: material.morphNormals,\n maxMorphTargets: renderer.maxMorphTargets,\n maxMorphNormals: renderer.maxMorphNormals,\n\n numDirLights: lights.directional.length,\n numPointLights: lights.point.length,\n numSpotLights: lights.spot.length,\n numRectAreaLights: lights.rectArea.length,\n numHemiLights: lights.hemi.length,\n\n numClippingPlanes: nClipPlanes,\n numClipIntersection: nClipIntersection,\n\n dithering: material.dithering,\n\n shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0,\n shadowMapType: renderer.shadowMap.type,\n\n toneMapping: renderer.toneMapping,\n physicallyCorrectLights: renderer.physicallyCorrectLights,\n\n premultipliedAlpha: material.premultipliedAlpha,\n\n alphaTest: material.alphaTest,\n doubleSided: material.side === DoubleSide,\n flipSided: material.side === BackSide,\n\n depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false\n\n };\n\n return parameters;\n\n };\n\n this.getProgramCode = function ( material, parameters ) {\n\n var array = [];\n\n if ( parameters.shaderID ) {\n\n array.push( parameters.shaderID );\n\n } else {\n\n array.push( material.fragmentShader );\n array.push( material.vertexShader );\n\n }\n\n if ( material.defines !== undefined ) {\n\n for ( var name in material.defines ) {\n\n array.push( name );\n array.push( material.defines[ name ] );\n\n }\n\n }\n\n for ( var i = 0; i < parameterNames.length; i ++ ) {\n\n array.push( parameters[ parameterNames[ i ] ] );\n\n }\n\n array.push( material.onBeforeCompile.toString() );\n\n array.push( renderer.gammaOutput );\n\n return array.join();\n\n };\n\n this.acquireProgram = function ( material, shader, parameters, code ) {\n\n var program;\n\n // Check if code has been already compiled\n for ( var p = 0, pl = programs.length; p < pl; p ++ ) {\n\n var programInfo = programs[ p ];\n\n if ( programInfo.code === code ) {\n\n program = programInfo;\n ++ program.usedTimes;\n\n break;\n\n }\n\n }\n\n if ( program === undefined ) {\n\n program = new WebGLProgram( renderer, extensions, code, material, shader, parameters );\n programs.push( program );\n\n }\n\n return program;\n\n };\n\n this.releaseProgram = function ( program ) {\n\n if ( -- program.usedTimes === 0 ) {\n\n // Remove from unordered set\n var i = programs.indexOf( program );\n programs[ i ] = programs[ programs.length - 1 ];\n programs.pop();\n\n // Free WebGL resources\n program.destroy();\n\n }\n\n };\n\n // Exposed for resource monitoring & error feedback via renderer.info:\n this.programs = programs;\n\n }\n\n /**\n * @author fordacious / fordacious.github.io\n */\n\n function WebGLProperties() {\n\n var properties = new WeakMap();\n\n function get( object ) {\n\n var map = properties.get( object );\n\n if ( map === undefined ) {\n\n map = {};\n properties.set( object, map );\n\n }\n\n return map;\n\n }\n\n function remove( object ) {\n\n properties.delete( object );\n\n }\n\n function update( object, key, value ) {\n\n properties.get( object )[ key ] = value;\n\n }\n\n function dispose() {\n\n properties = new WeakMap();\n\n }\n\n return {\n get: get,\n remove: remove,\n update: update,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function painterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } else if ( a.program && b.program && a.program !== b.program ) {\n\n return a.program.id - b.program.id;\n\n } else if ( a.material.id !== b.material.id ) {\n\n return a.material.id - b.material.id;\n\n } else if ( a.z !== b.z ) {\n\n return a.z - b.z;\n\n } else {\n\n return a.id - b.id;\n\n }\n\n }\n\n function reversePainterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } if ( a.z !== b.z ) {\n\n return b.z - a.z;\n\n } else {\n\n return a.id - b.id;\n\n }\n\n }\n\n function WebGLRenderList() {\n\n var renderItems = [];\n var renderItemsIndex = 0;\n\n var opaque = [];\n var transparent = [];\n\n function init() {\n\n renderItemsIndex = 0;\n\n opaque.length = 0;\n transparent.length = 0;\n\n }\n\n function push( object, geometry, material, z, group ) {\n\n var renderItem = renderItems[ renderItemsIndex ];\n\n if ( renderItem === undefined ) {\n\n renderItem = {\n id: object.id,\n object: object,\n geometry: geometry,\n material: material,\n program: material.program,\n renderOrder: object.renderOrder,\n z: z,\n group: group\n };\n\n renderItems[ renderItemsIndex ] = renderItem;\n\n } else {\n\n renderItem.id = object.id;\n renderItem.object = object;\n renderItem.geometry = geometry;\n renderItem.material = material;\n renderItem.program = material.program;\n renderItem.renderOrder = object.renderOrder;\n renderItem.z = z;\n renderItem.group = group;\n\n }\n\n ( material.transparent === true ? transparent : opaque ).push( renderItem );\n\n renderItemsIndex ++;\n\n }\n\n function sort() {\n\n if ( opaque.length > 1 ) opaque.sort( painterSortStable );\n if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );\n\n }\n\n return {\n opaque: opaque,\n transparent: transparent,\n\n init: init,\n push: push,\n\n sort: sort\n };\n\n }\n\n function WebGLRenderLists() {\n\n var lists = {};\n\n function get( scene, camera ) {\n\n var hash = scene.id + ',' + camera.id;\n var list = lists[ hash ];\n\n if ( list === undefined ) {\n\n // console.log( 'THREE.WebGLRenderLists:', hash );\n\n list = new WebGLRenderList();\n lists[ hash ] = list;\n\n }\n\n return list;\n\n }\n\n function dispose() {\n\n lists = {};\n\n }\n\n return {\n get: get,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function UniformsCache() {\n\n var lights = {};\n\n return {\n\n get: function ( light ) {\n\n if ( lights[ light.id ] !== undefined ) {\n\n return lights[ light.id ];\n\n }\n\n var uniforms;\n\n switch ( light.type ) {\n\n case 'DirectionalLight':\n uniforms = {\n direction: new Vector3(),\n color: new Color(),\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2()\n };\n break;\n\n case 'SpotLight':\n uniforms = {\n position: new Vector3(),\n direction: new Vector3(),\n color: new Color(),\n distance: 0,\n coneCos: 0,\n penumbraCos: 0,\n decay: 0,\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2()\n };\n break;\n\n case 'PointLight':\n uniforms = {\n position: new Vector3(),\n color: new Color(),\n distance: 0,\n decay: 0,\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2(),\n shadowCameraNear: 1,\n shadowCameraFar: 1000\n };\n break;\n\n case 'HemisphereLight':\n uniforms = {\n direction: new Vector3(),\n skyColor: new Color(),\n groundColor: new Color()\n };\n break;\n\n case 'RectAreaLight':\n uniforms = {\n color: new Color(),\n position: new Vector3(),\n halfWidth: new Vector3(),\n halfHeight: new Vector3()\n // TODO (abelnation): set RectAreaLight shadow uniforms\n };\n break;\n\n }\n\n lights[ light.id ] = uniforms;\n\n return uniforms;\n\n }\n\n };\n\n }\n\n var count = 0;\n\n function WebGLLights() {\n\n var cache = new UniformsCache();\n\n var state = {\n\n id: count ++,\n\n hash: '',\n\n ambient: [ 0, 0, 0 ],\n directional: [],\n directionalShadowMap: [],\n directionalShadowMatrix: [],\n spot: [],\n spotShadowMap: [],\n spotShadowMatrix: [],\n rectArea: [],\n point: [],\n pointShadowMap: [],\n pointShadowMatrix: [],\n hemi: []\n\n };\n\n var vector3 = new Vector3();\n var matrix4 = new Matrix4();\n var matrix42 = new Matrix4();\n\n function setup( lights, shadows, camera ) {\n\n var r = 0, g = 0, b = 0;\n\n var directionalLength = 0;\n var pointLength = 0;\n var spotLength = 0;\n var rectAreaLength = 0;\n var hemiLength = 0;\n\n var viewMatrix = camera.matrixWorldInverse;\n\n for ( var i = 0, l = lights.length; i < l; i ++ ) {\n\n var light = lights[ i ];\n\n var color = light.color;\n var intensity = light.intensity;\n var distance = light.distance;\n\n var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;\n\n if ( light.isAmbientLight ) {\n\n r += color.r * intensity;\n g += color.g * intensity;\n b += color.b * intensity;\n\n } else if ( light.isDirectionalLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n vector3.setFromMatrixPosition( light.target.matrixWorld );\n uniforms.direction.sub( vector3 );\n uniforms.direction.transformDirection( viewMatrix );\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n\n }\n\n state.directionalShadowMap[ directionalLength ] = shadowMap;\n state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;\n state.directional[ directionalLength ] = uniforms;\n\n directionalLength ++;\n\n } else if ( light.isSpotLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n uniforms.color.copy( color ).multiplyScalar( intensity );\n uniforms.distance = distance;\n\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n vector3.setFromMatrixPosition( light.target.matrixWorld );\n uniforms.direction.sub( vector3 );\n uniforms.direction.transformDirection( viewMatrix );\n\n uniforms.coneCos = Math.cos( light.angle );\n uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );\n uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n\n }\n\n state.spotShadowMap[ spotLength ] = shadowMap;\n state.spotShadowMatrix[ spotLength ] = light.shadow.matrix;\n state.spot[ spotLength ] = uniforms;\n\n spotLength ++;\n\n } else if ( light.isRectAreaLight ) {\n\n var uniforms = cache.get( light );\n\n // (a) intensity is the total visible light emitted\n //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) );\n\n // (b) intensity is the brightness of the light\n uniforms.color.copy( color ).multiplyScalar( intensity );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n // extract local rotation of light to derive width/height half vectors\n matrix42.identity();\n matrix4.copy( light.matrixWorld );\n matrix4.premultiply( viewMatrix );\n matrix42.extractRotation( matrix4 );\n\n uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n uniforms.halfWidth.applyMatrix4( matrix42 );\n uniforms.halfHeight.applyMatrix4( matrix42 );\n\n // TODO (abelnation): RectAreaLight distance?\n // uniforms.distance = distance;\n\n state.rectArea[ rectAreaLength ] = uniforms;\n\n rectAreaLength ++;\n\n } else if ( light.isPointLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n uniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n uniforms.distance = light.distance;\n uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n uniforms.shadowCameraNear = shadow.camera.near;\n uniforms.shadowCameraFar = shadow.camera.far;\n\n }\n\n state.pointShadowMap[ pointLength ] = shadowMap;\n state.pointShadowMatrix[ pointLength ] = light.shadow.matrix;\n state.point[ pointLength ] = uniforms;\n\n pointLength ++;\n\n } else if ( light.isHemisphereLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n uniforms.direction.transformDirection( viewMatrix );\n uniforms.direction.normalize();\n\n uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );\n uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );\n\n state.hemi[ hemiLength ] = uniforms;\n\n hemiLength ++;\n\n }\n\n }\n\n state.ambient[ 0 ] = r;\n state.ambient[ 1 ] = g;\n state.ambient[ 2 ] = b;\n\n state.directional.length = directionalLength;\n state.spot.length = spotLength;\n state.rectArea.length = rectAreaLength;\n state.point.length = pointLength;\n state.hemi.length = hemiLength;\n\n state.hash = state.id + ',' + directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length;\n\n }\n\n return {\n setup: setup,\n state: state\n };\n\n }\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WebGLRenderState() {\n\n var lights = new WebGLLights();\n\n var lightsArray = [];\n var shadowsArray = [];\n var spritesArray = [];\n\n function init() {\n\n lightsArray.length = 0;\n shadowsArray.length = 0;\n spritesArray.length = 0;\n\n }\n\n function pushLight( light ) {\n\n lightsArray.push( light );\n\n }\n\n function pushShadow( shadowLight ) {\n\n shadowsArray.push( shadowLight );\n\n }\n\n function pushSprite( shadowLight ) {\n\n spritesArray.push( shadowLight );\n\n }\n\n function setupLights( camera ) {\n\n lights.setup( lightsArray, shadowsArray, camera );\n\n }\n\n var state = {\n lightsArray: lightsArray,\n shadowsArray: shadowsArray,\n spritesArray: spritesArray,\n\n lights: lights\n };\n\n return {\n init: init,\n state: state,\n setupLights: setupLights,\n\n pushLight: pushLight,\n pushShadow: pushShadow,\n pushSprite: pushSprite\n };\n\n }\n\n function WebGLRenderStates() {\n\n var renderStates = {};\n\n function get( scene, camera ) {\n\n var hash = scene.id + ',' + camera.id;\n\n var renderState = renderStates[ hash ];\n\n if ( renderState === undefined ) {\n\n renderState = new WebGLRenderState();\n renderStates[ hash ] = renderState;\n\n }\n\n return renderState;\n\n }\n\n function dispose() {\n\n renderStates = {};\n\n }\n\n return {\n get: get,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author bhouston / https://clara.io\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n *\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>\n * }\n */\n\n function MeshDepthMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshDepthMaterial';\n\n this.depthPacking = BasicDepthPacking;\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.map = null;\n\n this.alphaMap = null;\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshDepthMaterial.prototype = Object.create( Material.prototype );\n MeshDepthMaterial.prototype.constructor = MeshDepthMaterial;\n\n MeshDepthMaterial.prototype.isMeshDepthMaterial = true;\n\n MeshDepthMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.depthPacking = source.depthPacking;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n this.map = source.map;\n\n this.alphaMap = source.alphaMap;\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n *\n * referencePosition: <float>,\n * nearDistance: <float>,\n * farDistance: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>\n *\n * }\n */\n\n function MeshDistanceMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshDistanceMaterial';\n\n this.referencePosition = new Vector3();\n this.nearDistance = 1;\n this.farDistance = 1000;\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.map = null;\n\n this.alphaMap = null;\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshDistanceMaterial.prototype = Object.create( Material.prototype );\n MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial;\n\n MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true;\n\n MeshDistanceMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.referencePosition.copy( source.referencePosition );\n this.nearDistance = source.nearDistance;\n this.farDistance = source.farDistance;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n this.map = source.map;\n\n this.alphaMap = source.alphaMap;\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {\n\n var _frustum = new Frustum(),\n _projScreenMatrix = new Matrix4(),\n\n _shadowMapSize = new Vector2(),\n _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ),\n\n _lookTarget = new Vector3(),\n _lightPositionWorld = new Vector3(),\n\n _MorphingFlag = 1,\n _SkinningFlag = 2,\n\n _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,\n\n _depthMaterials = new Array( _NumberOfMaterialVariants ),\n _distanceMaterials = new Array( _NumberOfMaterialVariants ),\n\n _materialCache = {};\n\n var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide };\n\n var cubeDirections = [\n new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),\n new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )\n ];\n\n var cubeUps = [\n new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),\n new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 )\n ];\n\n var cube2DViewPorts = [\n new Vector4(), new Vector4(), new Vector4(),\n new Vector4(), new Vector4(), new Vector4()\n ];\n\n // init\n\n for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {\n\n var useMorphing = ( i & _MorphingFlag ) !== 0;\n var useSkinning = ( i & _SkinningFlag ) !== 0;\n\n var depthMaterial = new MeshDepthMaterial( {\n\n depthPacking: RGBADepthPacking,\n\n morphTargets: useMorphing,\n skinning: useSkinning\n\n } );\n\n _depthMaterials[ i ] = depthMaterial;\n\n //\n\n var distanceMaterial = new MeshDistanceMaterial( {\n\n morphTargets: useMorphing,\n skinning: useSkinning\n\n } );\n\n _distanceMaterials[ i ] = distanceMaterial;\n\n }\n\n //\n\n var scope = this;\n\n this.enabled = false;\n\n this.autoUpdate = true;\n this.needsUpdate = false;\n\n this.type = PCFShadowMap;\n\n this.render = function ( lights, scene, camera ) {\n\n if ( scope.enabled === false ) return;\n if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;\n\n if ( lights.length === 0 ) return;\n\n // TODO Clean up (needed in case of contextlost)\n var _gl = _renderer.context;\n var _state = _renderer.state;\n\n // Set GL state for depth map.\n _state.disable( _gl.BLEND );\n _state.buffers.color.setClear( 1, 1, 1, 1 );\n _state.buffers.depth.setTest( true );\n _state.setScissorTest( false );\n\n // render depth map\n\n var faceCount;\n\n for ( var i = 0, il = lights.length; i < il; i ++ ) {\n\n var light = lights[ i ];\n var shadow = light.shadow;\n var isPointLight = light && light.isPointLight;\n\n if ( shadow === undefined ) {\n\n console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );\n continue;\n\n }\n\n var shadowCamera = shadow.camera;\n\n _shadowMapSize.copy( shadow.mapSize );\n _shadowMapSize.min( _maxShadowMapSize );\n\n if ( isPointLight ) {\n\n var vpWidth = _shadowMapSize.x;\n var vpHeight = _shadowMapSize.y;\n\n // These viewports map a cube-map onto a 2D texture with the\n // following orientation:\n //\n // xzXZ\n // y Y\n //\n // X - Positive x direction\n // x - Negative x direction\n // Y - Positive y direction\n // y - Negative y direction\n // Z - Positive z direction\n // z - Negative z direction\n\n // positive X\n cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );\n // negative X\n cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );\n // positive Z\n cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );\n // negative Z\n cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );\n // positive Y\n cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );\n // negative Y\n cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );\n\n _shadowMapSize.x *= 4.0;\n _shadowMapSize.y *= 2.0;\n\n }\n\n if ( shadow.map === null ) {\n\n var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };\n\n shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );\n shadow.map.texture.name = light.name + \".shadowMap\";\n\n shadowCamera.updateProjectionMatrix();\n\n }\n\n if ( shadow.isSpotLightShadow ) {\n\n shadow.update( light );\n\n }\n\n var shadowMap = shadow.map;\n var shadowMatrix = shadow.matrix;\n\n _lightPositionWorld.setFromMatrixPosition( light.matrixWorld );\n shadowCamera.position.copy( _lightPositionWorld );\n\n if ( isPointLight ) {\n\n faceCount = 6;\n\n // for point lights we set the shadow matrix to be a translation-only matrix\n // equal to inverse of the light's position\n\n shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z );\n\n } else {\n\n faceCount = 1;\n\n _lookTarget.setFromMatrixPosition( light.target.matrixWorld );\n shadowCamera.lookAt( _lookTarget );\n shadowCamera.updateMatrixWorld();\n\n // compute shadow matrix\n\n shadowMatrix.set(\n 0.5, 0.0, 0.0, 0.5,\n 0.0, 0.5, 0.0, 0.5,\n 0.0, 0.0, 0.5, 0.5,\n 0.0, 0.0, 0.0, 1.0\n );\n\n shadowMatrix.multiply( shadowCamera.projectionMatrix );\n shadowMatrix.multiply( shadowCamera.matrixWorldInverse );\n\n }\n\n _renderer.setRenderTarget( shadowMap );\n _renderer.clear();\n\n // render shadow map for each cube face (if omni-directional) or\n // run a single pass if not\n\n for ( var face = 0; face < faceCount; face ++ ) {\n\n if ( isPointLight ) {\n\n _lookTarget.copy( shadowCamera.position );\n _lookTarget.add( cubeDirections[ face ] );\n shadowCamera.up.copy( cubeUps[ face ] );\n shadowCamera.lookAt( _lookTarget );\n shadowCamera.updateMatrixWorld();\n\n var vpDimensions = cube2DViewPorts[ face ];\n _state.viewport( vpDimensions );\n\n }\n\n // update camera matrices and frustum\n\n _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );\n _frustum.setFromMatrix( _projScreenMatrix );\n\n // set object matrices & frustum culling\n\n renderObject( scene, camera, shadowCamera, isPointLight );\n\n }\n\n }\n\n scope.needsUpdate = false;\n\n };\n\n function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) {\n\n var geometry = object.geometry;\n\n var result = null;\n\n var materialVariants = _depthMaterials;\n var customMaterial = object.customDepthMaterial;\n\n if ( isPointLight ) {\n\n materialVariants = _distanceMaterials;\n customMaterial = object.customDistanceMaterial;\n\n }\n\n if ( ! customMaterial ) {\n\n var useMorphing = false;\n\n if ( material.morphTargets ) {\n\n if ( geometry && geometry.isBufferGeometry ) {\n\n useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;\n\n } else if ( geometry && geometry.isGeometry ) {\n\n useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0;\n\n }\n\n }\n\n if ( object.isSkinnedMesh && material.skinning === false ) {\n\n console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object );\n\n }\n\n var useSkinning = object.isSkinnedMesh && material.skinning;\n\n var variantIndex = 0;\n\n if ( useMorphing ) variantIndex |= _MorphingFlag;\n if ( useSkinning ) variantIndex |= _SkinningFlag;\n\n result = materialVariants[ variantIndex ];\n\n } else {\n\n result = customMaterial;\n\n }\n\n if ( _renderer.localClippingEnabled &&\n material.clipShadows === true &&\n material.clippingPlanes.length !== 0 ) {\n\n // in this case we need a unique material instance reflecting the\n // appropriate state\n\n var keyA = result.uuid, keyB = material.uuid;\n\n var materialsForVariant = _materialCache[ keyA ];\n\n if ( materialsForVariant === undefined ) {\n\n materialsForVariant = {};\n _materialCache[ keyA ] = materialsForVariant;\n\n }\n\n var cachedMaterial = materialsForVariant[ keyB ];\n\n if ( cachedMaterial === undefined ) {\n\n cachedMaterial = result.clone();\n materialsForVariant[ keyB ] = cachedMaterial;\n\n }\n\n result = cachedMaterial;\n\n }\n\n result.visible = material.visible;\n result.wireframe = material.wireframe;\n\n result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ];\n\n result.clipShadows = material.clipShadows;\n result.clippingPlanes = material.clippingPlanes;\n result.clipIntersection = material.clipIntersection;\n\n result.wireframeLinewidth = material.wireframeLinewidth;\n result.linewidth = material.linewidth;\n\n if ( isPointLight && result.isMeshDistanceMaterial ) {\n\n result.referencePosition.copy( lightPositionWorld );\n result.nearDistance = shadowCameraNear;\n result.farDistance = shadowCameraFar;\n\n }\n\n return result;\n\n }\n\n function renderObject( object, camera, shadowCamera, isPointLight ) {\n\n if ( object.visible === false ) return;\n\n var visible = object.layers.test( camera.layers );\n\n if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {\n\n if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {\n\n object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );\n\n var geometry = _objects.update( object );\n var material = object.material;\n\n if ( Array.isArray( material ) ) {\n\n var groups = geometry.groups;\n\n for ( var k = 0, kl = groups.length; k < kl; k ++ ) {\n\n var group = groups[ k ];\n var groupMaterial = material[ group.materialIndex ];\n\n if ( groupMaterial && groupMaterial.visible ) {\n\n var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );\n _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );\n\n }\n\n }\n\n } else if ( material.visible ) {\n\n var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );\n _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );\n\n }\n\n }\n\n }\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n renderObject( children[ i ], camera, shadowCamera, isPointLight );\n\n }\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.needsUpdate = true;\n\n }\n\n CanvasTexture.prototype = Object.create( Texture.prototype );\n CanvasTexture.prototype.constructor = CanvasTexture;\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {\n\n var vertexBuffer, elementBuffer;\n var program, attributes, uniforms;\n\n var texture;\n\n // decompose matrixWorld\n\n var spritePosition = new Vector3();\n var spriteRotation = new Quaternion();\n var spriteScale = new Vector3();\n\n function init() {\n\n var vertices = new Float32Array( [\n - 0.5, - 0.5, 0, 0,\n 0.5, - 0.5, 1, 0,\n 0.5, 0.5, 1, 1,\n - 0.5, 0.5, 0, 1\n ] );\n\n var faces = new Uint16Array( [\n 0, 1, 2,\n 0, 2, 3\n ] );\n\n vertexBuffer = gl.createBuffer();\n elementBuffer = gl.createBuffer();\n\n gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );\n\n gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );\n\n program = createProgram();\n\n attributes = {\n position: gl.getAttribLocation( program, 'position' ),\n uv: gl.getAttribLocation( program, 'uv' )\n };\n\n uniforms = {\n uvOffset: gl.getUniformLocation( program, 'uvOffset' ),\n uvScale: gl.getUniformLocation( program, 'uvScale' ),\n\n rotation: gl.getUniformLocation( program, 'rotation' ),\n center: gl.getUniformLocation( program, 'center' ),\n scale: gl.getUniformLocation( program, 'scale' ),\n\n color: gl.getUniformLocation( program, 'color' ),\n map: gl.getUniformLocation( program, 'map' ),\n opacity: gl.getUniformLocation( program, 'opacity' ),\n\n modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),\n projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),\n\n fogType: gl.getUniformLocation( program, 'fogType' ),\n fogDensity: gl.getUniformLocation( program, 'fogDensity' ),\n fogNear: gl.getUniformLocation( program, 'fogNear' ),\n fogFar: gl.getUniformLocation( program, 'fogFar' ),\n fogColor: gl.getUniformLocation( program, 'fogColor' ),\n fogDepth: gl.getUniformLocation( program, 'fogDepth' ),\n\n alphaTest: gl.getUniformLocation( program, 'alphaTest' )\n };\n\n var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = 8;\n canvas.height = 8;\n\n var context = canvas.getContext( '2d' );\n context.fillStyle = 'white';\n context.fillRect( 0, 0, 8, 8 );\n\n texture = new CanvasTexture( canvas );\n\n }\n\n this.render = function ( sprites, scene, camera ) {\n\n if ( sprites.length === 0 ) return;\n\n // setup gl\n\n if ( program === undefined ) {\n\n init();\n\n }\n\n state.useProgram( program );\n\n state.initAttributes();\n state.enableAttribute( attributes.position );\n state.enableAttribute( attributes.uv );\n state.disableUnusedAttributes();\n\n state.disable( gl.CULL_FACE );\n state.enable( gl.BLEND );\n\n gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );\n gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );\n\n gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\n gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );\n\n state.activeTexture( gl.TEXTURE0 );\n gl.uniform1i( uniforms.map, 0 );\n\n var oldFogType = 0;\n var sceneFogType = 0;\n var fog = scene.fog;\n\n if ( fog ) {\n\n gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );\n\n if ( fog.isFog ) {\n\n gl.uniform1f( uniforms.fogNear, fog.near );\n gl.uniform1f( uniforms.fogFar, fog.far );\n\n gl.uniform1i( uniforms.fogType, 1 );\n oldFogType = 1;\n sceneFogType = 1;\n\n } else if ( fog.isFogExp2 ) {\n\n gl.uniform1f( uniforms.fogDensity, fog.density );\n\n gl.uniform1i( uniforms.fogType, 2 );\n oldFogType = 2;\n sceneFogType = 2;\n\n }\n\n } else {\n\n gl.uniform1i( uniforms.fogType, 0 );\n oldFogType = 0;\n sceneFogType = 0;\n\n }\n\n\n // update positions and sort\n\n for ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n var sprite = sprites[ i ];\n\n sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );\n sprite.z = - sprite.modelViewMatrix.elements[ 14 ];\n\n }\n\n sprites.sort( painterSortStable );\n\n // render all sprites\n\n var scale = [];\n var center = [];\n\n for ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n var sprite = sprites[ i ];\n var material = sprite.material;\n\n if ( material.visible === false ) continue;\n\n sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined );\n\n gl.uniform1f( uniforms.alphaTest, material.alphaTest );\n gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );\n\n sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );\n\n scale[ 0 ] = spriteScale.x;\n scale[ 1 ] = spriteScale.y;\n\n center[ 0 ] = sprite.center.x - 0.5;\n center[ 1 ] = sprite.center.y - 0.5;\n\n var fogType = 0;\n\n if ( scene.fog && material.fog ) {\n\n fogType = sceneFogType;\n\n }\n\n if ( oldFogType !== fogType ) {\n\n gl.uniform1i( uniforms.fogType, fogType );\n oldFogType = fogType;\n\n }\n\n if ( material.map !== null ) {\n\n gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );\n gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );\n\n } else {\n\n gl.uniform2f( uniforms.uvOffset, 0, 0 );\n gl.uniform2f( uniforms.uvScale, 1, 1 );\n\n }\n\n gl.uniform1f( uniforms.opacity, material.opacity );\n gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );\n\n gl.uniform1f( uniforms.rotation, material.rotation );\n gl.uniform2fv( uniforms.center, center );\n gl.uniform2fv( uniforms.scale, scale );\n\n state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );\n state.buffers.depth.setTest( material.depthTest );\n state.buffers.depth.setMask( material.depthWrite );\n state.buffers.color.setMask( material.colorWrite );\n\n textures.setTexture2D( material.map || texture, 0 );\n\n gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined );\n\n }\n\n // restore gl\n\n state.enable( gl.CULL_FACE );\n\n state.reset();\n\n };\n\n function createProgram() {\n\n var program = gl.createProgram();\n\n var vertexShader = gl.createShader( gl.VERTEX_SHADER );\n var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );\n\n gl.shaderSource( vertexShader, [\n\n 'precision ' + capabilities.precision + ' float;',\n\n '#define SHADER_NAME ' + 'SpriteMaterial',\n\n 'uniform mat4 modelViewMatrix;',\n 'uniform mat4 projectionMatrix;',\n 'uniform float rotation;',\n 'uniform vec2 center;',\n 'uniform vec2 scale;',\n 'uniform vec2 uvOffset;',\n 'uniform vec2 uvScale;',\n\n 'attribute vec2 position;',\n 'attribute vec2 uv;',\n\n 'varying vec2 vUV;',\n 'varying float fogDepth;',\n\n 'void main() {',\n\n ' vUV = uvOffset + uv * uvScale;',\n\n ' vec2 alignedPosition = ( position - center ) * scale;',\n\n ' vec2 rotatedPosition;',\n ' rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',\n ' rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',\n\n ' vec4 mvPosition;',\n\n ' mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',\n ' mvPosition.xy += rotatedPosition;',\n\n ' gl_Position = projectionMatrix * mvPosition;',\n\n ' fogDepth = - mvPosition.z;',\n\n '}'\n\n ].join( '\\n' ) );\n\n gl.shaderSource( fragmentShader, [\n\n 'precision ' + capabilities.precision + ' float;',\n\n '#define SHADER_NAME ' + 'SpriteMaterial',\n\n 'uniform vec3 color;',\n 'uniform sampler2D map;',\n 'uniform float opacity;',\n\n 'uniform int fogType;',\n 'uniform vec3 fogColor;',\n 'uniform float fogDensity;',\n 'uniform float fogNear;',\n 'uniform float fogFar;',\n 'uniform float alphaTest;',\n\n 'varying vec2 vUV;',\n 'varying float fogDepth;',\n\n 'void main() {',\n\n ' vec4 texture = texture2D( map, vUV );',\n\n ' gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',\n\n ' if ( gl_FragColor.a < alphaTest ) discard;',\n\n ' if ( fogType > 0 ) {',\n\n ' float fogFactor = 0.0;',\n\n ' if ( fogType == 1 ) {',\n\n ' fogFactor = smoothstep( fogNear, fogFar, fogDepth );',\n\n ' } else {',\n\n ' const float LOG2 = 1.442695;',\n ' fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );',\n ' fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',\n\n ' }',\n\n ' gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );',\n\n ' }',\n\n '}'\n\n ].join( '\\n' ) );\n\n gl.compileShader( vertexShader );\n gl.compileShader( fragmentShader );\n\n gl.attachShader( program, vertexShader );\n gl.attachShader( program, fragmentShader );\n\n gl.linkProgram( program );\n\n return program;\n\n }\n\n function painterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } else if ( a.z !== b.z ) {\n\n return b.z - a.z;\n\n } else {\n\n return b.id - a.id;\n\n }\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLState( gl, extensions, utils ) {\n\n function ColorBuffer() {\n\n var locked = false;\n\n var color = new Vector4();\n var currentColorMask = null;\n var currentColorClear = new Vector4( 0, 0, 0, 0 );\n\n return {\n\n setMask: function ( colorMask ) {\n\n if ( currentColorMask !== colorMask && ! locked ) {\n\n gl.colorMask( colorMask, colorMask, colorMask, colorMask );\n currentColorMask = colorMask;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( r, g, b, a, premultipliedAlpha ) {\n\n if ( premultipliedAlpha === true ) {\n\n r *= a; g *= a; b *= a;\n\n }\n\n color.set( r, g, b, a );\n\n if ( currentColorClear.equals( color ) === false ) {\n\n gl.clearColor( r, g, b, a );\n currentColorClear.copy( color );\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentColorMask = null;\n currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state\n\n }\n\n };\n\n }\n\n function DepthBuffer() {\n\n var locked = false;\n\n var currentDepthMask = null;\n var currentDepthFunc = null;\n var currentDepthClear = null;\n\n return {\n\n setTest: function ( depthTest ) {\n\n if ( depthTest ) {\n\n enable( gl.DEPTH_TEST );\n\n } else {\n\n disable( gl.DEPTH_TEST );\n\n }\n\n },\n\n setMask: function ( depthMask ) {\n\n if ( currentDepthMask !== depthMask && ! locked ) {\n\n gl.depthMask( depthMask );\n currentDepthMask = depthMask;\n\n }\n\n },\n\n setFunc: function ( depthFunc ) {\n\n if ( currentDepthFunc !== depthFunc ) {\n\n if ( depthFunc ) {\n\n switch ( depthFunc ) {\n\n case NeverDepth:\n\n gl.depthFunc( gl.NEVER );\n break;\n\n case AlwaysDepth:\n\n gl.depthFunc( gl.ALWAYS );\n break;\n\n case LessDepth:\n\n gl.depthFunc( gl.LESS );\n break;\n\n case LessEqualDepth:\n\n gl.depthFunc( gl.LEQUAL );\n break;\n\n case EqualDepth:\n\n gl.depthFunc( gl.EQUAL );\n break;\n\n case GreaterEqualDepth:\n\n gl.depthFunc( gl.GEQUAL );\n break;\n\n case GreaterDepth:\n\n gl.depthFunc( gl.GREATER );\n break;\n\n case NotEqualDepth:\n\n gl.depthFunc( gl.NOTEQUAL );\n break;\n\n default:\n\n gl.depthFunc( gl.LEQUAL );\n\n }\n\n } else {\n\n gl.depthFunc( gl.LEQUAL );\n\n }\n\n currentDepthFunc = depthFunc;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( depth ) {\n\n if ( currentDepthClear !== depth ) {\n\n gl.clearDepth( depth );\n currentDepthClear = depth;\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentDepthMask = null;\n currentDepthFunc = null;\n currentDepthClear = null;\n\n }\n\n };\n\n }\n\n function StencilBuffer() {\n\n var locked = false;\n\n var currentStencilMask = null;\n var currentStencilFunc = null;\n var currentStencilRef = null;\n var currentStencilFuncMask = null;\n var currentStencilFail = null;\n var currentStencilZFail = null;\n var currentStencilZPass = null;\n var currentStencilClear = null;\n\n return {\n\n setTest: function ( stencilTest ) {\n\n if ( stencilTest ) {\n\n enable( gl.STENCIL_TEST );\n\n } else {\n\n disable( gl.STENCIL_TEST );\n\n }\n\n },\n\n setMask: function ( stencilMask ) {\n\n if ( currentStencilMask !== stencilMask && ! locked ) {\n\n gl.stencilMask( stencilMask );\n currentStencilMask = stencilMask;\n\n }\n\n },\n\n setFunc: function ( stencilFunc, stencilRef, stencilMask ) {\n\n if ( currentStencilFunc !== stencilFunc ||\n currentStencilRef !== stencilRef ||\n currentStencilFuncMask !== stencilMask ) {\n\n gl.stencilFunc( stencilFunc, stencilRef, stencilMask );\n\n currentStencilFunc = stencilFunc;\n currentStencilRef = stencilRef;\n currentStencilFuncMask = stencilMask;\n\n }\n\n },\n\n setOp: function ( stencilFail, stencilZFail, stencilZPass ) {\n\n if ( currentStencilFail !== stencilFail ||\n currentStencilZFail !== stencilZFail ||\n currentStencilZPass !== stencilZPass ) {\n\n gl.stencilOp( stencilFail, stencilZFail, stencilZPass );\n\n currentStencilFail = stencilFail;\n currentStencilZFail = stencilZFail;\n currentStencilZPass = stencilZPass;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( stencil ) {\n\n if ( currentStencilClear !== stencil ) {\n\n gl.clearStencil( stencil );\n currentStencilClear = stencil;\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentStencilMask = null;\n currentStencilFunc = null;\n currentStencilRef = null;\n currentStencilFuncMask = null;\n currentStencilFail = null;\n currentStencilZFail = null;\n currentStencilZPass = null;\n currentStencilClear = null;\n\n }\n\n };\n\n }\n\n //\n\n var colorBuffer = new ColorBuffer();\n var depthBuffer = new DepthBuffer();\n var stencilBuffer = new StencilBuffer();\n\n var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n var newAttributes = new Uint8Array( maxVertexAttributes );\n var enabledAttributes = new Uint8Array( maxVertexAttributes );\n var attributeDivisors = new Uint8Array( maxVertexAttributes );\n\n var capabilities = {};\n\n var compressedTextureFormats = null;\n\n var currentProgram = null;\n\n var currentBlending = null;\n var currentBlendEquation = null;\n var currentBlendSrc = null;\n var currentBlendDst = null;\n var currentBlendEquationAlpha = null;\n var currentBlendSrcAlpha = null;\n var currentBlendDstAlpha = null;\n var currentPremultipledAlpha = false;\n\n var currentFlipSided = null;\n var currentCullFace = null;\n\n var currentLineWidth = null;\n\n var currentPolygonOffsetFactor = null;\n var currentPolygonOffsetUnits = null;\n\n var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );\n\n var lineWidthAvailable = false;\n var version = 0;\n var glVersion = gl.getParameter( gl.VERSION );\n\n if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {\n\n version = parseFloat( /^WebGL\\ ([0-9])/.exec( glVersion )[ 1 ] );\n lineWidthAvailable = ( version >= 1.0 );\n\n } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {\n\n version = parseFloat( /^OpenGL\\ ES\\ ([0-9])/.exec( glVersion )[ 1 ] );\n lineWidthAvailable = ( version >= 2.0 );\n\n }\n\n var currentTextureSlot = null;\n var currentBoundTextures = {};\n\n var currentScissor = new Vector4();\n var currentViewport = new Vector4();\n\n function createTexture( type, target, count ) {\n\n var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.\n var texture = gl.createTexture();\n\n gl.bindTexture( type, texture );\n gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\n for ( var i = 0; i < count; i ++ ) {\n\n gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n }\n\n return texture;\n\n }\n\n var emptyTextures = {};\n emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );\n emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );\n\n // init\n\n colorBuffer.setClear( 0, 0, 0, 1 );\n depthBuffer.setClear( 1 );\n stencilBuffer.setClear( 0 );\n\n enable( gl.DEPTH_TEST );\n depthBuffer.setFunc( LessEqualDepth );\n\n setFlipSided( false );\n setCullFace( CullFaceBack );\n enable( gl.CULL_FACE );\n\n enable( gl.BLEND );\n setBlending( NormalBlending );\n\n //\n\n function initAttributes() {\n\n for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {\n\n newAttributes[ i ] = 0;\n\n }\n\n }\n\n function enableAttribute( attribute ) {\n\n newAttributes[ attribute ] = 1;\n\n if ( enabledAttributes[ attribute ] === 0 ) {\n\n gl.enableVertexAttribArray( attribute );\n enabledAttributes[ attribute ] = 1;\n\n }\n\n if ( attributeDivisors[ attribute ] !== 0 ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n extension.vertexAttribDivisorANGLE( attribute, 0 );\n attributeDivisors[ attribute ] = 0;\n\n }\n\n }\n\n function enableAttributeAndDivisor( attribute, meshPerAttribute ) {\n\n newAttributes[ attribute ] = 1;\n\n if ( enabledAttributes[ attribute ] === 0 ) {\n\n gl.enableVertexAttribArray( attribute );\n enabledAttributes[ attribute ] = 1;\n\n }\n\n if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );\n attributeDivisors[ attribute ] = meshPerAttribute;\n\n }\n\n }\n\n function disableUnusedAttributes() {\n\n for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {\n\n if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {\n\n gl.disableVertexAttribArray( i );\n enabledAttributes[ i ] = 0;\n\n }\n\n }\n\n }\n\n function enable( id ) {\n\n if ( capabilities[ id ] !== true ) {\n\n gl.enable( id );\n capabilities[ id ] = true;\n\n }\n\n }\n\n function disable( id ) {\n\n if ( capabilities[ id ] !== false ) {\n\n gl.disable( id );\n capabilities[ id ] = false;\n\n }\n\n }\n\n function getCompressedTextureFormats() {\n\n if ( compressedTextureFormats === null ) {\n\n compressedTextureFormats = [];\n\n if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||\n extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||\n extensions.get( 'WEBGL_compressed_texture_etc1' ) ||\n extensions.get( 'WEBGL_compressed_texture_astc' ) ) {\n\n var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );\n\n for ( var i = 0; i < formats.length; i ++ ) {\n\n compressedTextureFormats.push( formats[ i ] );\n\n }\n\n }\n\n }\n\n return compressedTextureFormats;\n\n }\n\n function useProgram( program ) {\n\n if ( currentProgram !== program ) {\n\n gl.useProgram( program );\n\n currentProgram = program;\n\n return true;\n\n }\n\n return false;\n\n }\n\n function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {\n\n if ( blending !== NoBlending ) {\n\n enable( gl.BLEND );\n\n } else {\n\n disable( gl.BLEND );\n\n }\n\n if ( blending !== CustomBlending ) {\n\n if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {\n\n switch ( blending ) {\n\n case AdditiveBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.SRC_ALPHA, gl.ONE );\n\n }\n break;\n\n case SubtractiveBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );\n\n }\n break;\n\n case MultiplyBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.ZERO, gl.SRC_COLOR );\n\n }\n break;\n\n default:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n } else {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n }\n\n }\n\n }\n\n currentBlendEquation = null;\n currentBlendSrc = null;\n currentBlendDst = null;\n currentBlendEquationAlpha = null;\n currentBlendSrcAlpha = null;\n currentBlendDstAlpha = null;\n\n } else {\n\n blendEquationAlpha = blendEquationAlpha || blendEquation;\n blendSrcAlpha = blendSrcAlpha || blendSrc;\n blendDstAlpha = blendDstAlpha || blendDst;\n\n if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {\n\n gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) );\n\n currentBlendEquation = blendEquation;\n currentBlendEquationAlpha = blendEquationAlpha;\n\n }\n\n if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {\n\n gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) );\n\n currentBlendSrc = blendSrc;\n currentBlendDst = blendDst;\n currentBlendSrcAlpha = blendSrcAlpha;\n currentBlendDstAlpha = blendDstAlpha;\n\n }\n\n }\n\n currentBlending = blending;\n currentPremultipledAlpha = premultipliedAlpha;\n\n }\n\n function setMaterial( material, frontFaceCW ) {\n\n material.side === DoubleSide\n ? disable( gl.CULL_FACE )\n : enable( gl.CULL_FACE );\n\n var flipSided = ( material.side === BackSide );\n if ( frontFaceCW ) flipSided = ! flipSided;\n\n setFlipSided( flipSided );\n\n material.transparent === true\n ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha )\n : setBlending( NoBlending );\n\n depthBuffer.setFunc( material.depthFunc );\n depthBuffer.setTest( material.depthTest );\n depthBuffer.setMask( material.depthWrite );\n colorBuffer.setMask( material.colorWrite );\n\n setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );\n\n }\n\n //\n\n function setFlipSided( flipSided ) {\n\n if ( currentFlipSided !== flipSided ) {\n\n if ( flipSided ) {\n\n gl.frontFace( gl.CW );\n\n } else {\n\n gl.frontFace( gl.CCW );\n\n }\n\n currentFlipSided = flipSided;\n\n }\n\n }\n\n function setCullFace( cullFace ) {\n\n if ( cullFace !== CullFaceNone ) {\n\n enable( gl.CULL_FACE );\n\n if ( cullFace !== currentCullFace ) {\n\n if ( cullFace === CullFaceBack ) {\n\n gl.cullFace( gl.BACK );\n\n } else if ( cullFace === CullFaceFront ) {\n\n gl.cullFace( gl.FRONT );\n\n } else {\n\n gl.cullFace( gl.FRONT_AND_BACK );\n\n }\n\n }\n\n } else {\n\n disable( gl.CULL_FACE );\n\n }\n\n currentCullFace = cullFace;\n\n }\n\n function setLineWidth( width ) {\n\n if ( width !== currentLineWidth ) {\n\n if ( lineWidthAvailable ) gl.lineWidth( width );\n\n currentLineWidth = width;\n\n }\n\n }\n\n function setPolygonOffset( polygonOffset, factor, units ) {\n\n if ( polygonOffset ) {\n\n enable( gl.POLYGON_OFFSET_FILL );\n\n if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {\n\n gl.polygonOffset( factor, units );\n\n currentPolygonOffsetFactor = factor;\n currentPolygonOffsetUnits = units;\n\n }\n\n } else {\n\n disable( gl.POLYGON_OFFSET_FILL );\n\n }\n\n }\n\n function setScissorTest( scissorTest ) {\n\n if ( scissorTest ) {\n\n enable( gl.SCISSOR_TEST );\n\n } else {\n\n disable( gl.SCISSOR_TEST );\n\n }\n\n }\n\n // texture\n\n function activeTexture( webglSlot ) {\n\n if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n if ( currentTextureSlot !== webglSlot ) {\n\n gl.activeTexture( webglSlot );\n currentTextureSlot = webglSlot;\n\n }\n\n }\n\n function bindTexture( webglType, webglTexture ) {\n\n if ( currentTextureSlot === null ) {\n\n activeTexture();\n\n }\n\n var boundTexture = currentBoundTextures[ currentTextureSlot ];\n\n if ( boundTexture === undefined ) {\n\n boundTexture = { type: undefined, texture: undefined };\n currentBoundTextures[ currentTextureSlot ] = boundTexture;\n\n }\n\n if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {\n\n gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );\n\n boundTexture.type = webglType;\n boundTexture.texture = webglTexture;\n\n }\n\n }\n\n function compressedTexImage2D() {\n\n try {\n\n gl.compressedTexImage2D.apply( gl, arguments );\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLState:', error );\n\n }\n\n }\n\n function texImage2D() {\n\n try {\n\n gl.texImage2D.apply( gl, arguments );\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLState:', error );\n\n }\n\n }\n\n //\n\n function scissor( scissor ) {\n\n if ( currentScissor.equals( scissor ) === false ) {\n\n gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );\n currentScissor.copy( scissor );\n\n }\n\n }\n\n function viewport( viewport ) {\n\n if ( currentViewport.equals( viewport ) === false ) {\n\n gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );\n currentViewport.copy( viewport );\n\n }\n\n }\n\n //\n\n function reset() {\n\n for ( var i = 0; i < enabledAttributes.length; i ++ ) {\n\n if ( enabledAttributes[ i ] === 1 ) {\n\n gl.disableVertexAttribArray( i );\n enabledAttributes[ i ] = 0;\n\n }\n\n }\n\n capabilities = {};\n\n compressedTextureFormats = null;\n\n currentTextureSlot = null;\n currentBoundTextures = {};\n\n currentProgram = null;\n\n currentBlending = null;\n\n currentFlipSided = null;\n currentCullFace = null;\n\n colorBuffer.reset();\n depthBuffer.reset();\n stencilBuffer.reset();\n\n }\n\n return {\n\n buffers: {\n color: colorBuffer,\n depth: depthBuffer,\n stencil: stencilBuffer\n },\n\n initAttributes: initAttributes,\n enableAttribute: enableAttribute,\n enableAttributeAndDivisor: enableAttributeAndDivisor,\n disableUnusedAttributes: disableUnusedAttributes,\n enable: enable,\n disable: disable,\n getCompressedTextureFormats: getCompressedTextureFormats,\n\n useProgram: useProgram,\n\n setBlending: setBlending,\n setMaterial: setMaterial,\n\n setFlipSided: setFlipSided,\n setCullFace: setCullFace,\n\n setLineWidth: setLineWidth,\n setPolygonOffset: setPolygonOffset,\n\n setScissorTest: setScissorTest,\n\n activeTexture: activeTexture,\n bindTexture: bindTexture,\n compressedTexImage2D: compressedTexImage2D,\n texImage2D: texImage2D,\n\n scissor: scissor,\n viewport: viewport,\n\n reset: reset\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {\n\n var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */\n var _videoTextures = {};\n var _canvas;\n\n //\n\n function clampToMaxSize( image, maxSize ) {\n\n if ( image.width > maxSize || image.height > maxSize ) {\n\n if ( 'data' in image ) {\n\n console.warn( 'THREE.WebGLRenderer: image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );\n return;\n\n }\n\n // Warning: Scaling through the canvas will only work with images that use\n // premultiplied alpha.\n\n var scale = maxSize / Math.max( image.width, image.height );\n\n var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = Math.floor( image.width * scale );\n canvas.height = Math.floor( image.height * scale );\n\n var context = canvas.getContext( '2d' );\n context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );\n\n console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );\n\n return canvas;\n\n }\n\n return image;\n\n }\n\n function isPowerOfTwo( image ) {\n\n return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height );\n\n }\n\n function makePowerOfTwo( image ) {\n\n if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) {\n\n if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\n _canvas.width = _Math.floorPowerOfTwo( image.width );\n _canvas.height = _Math.floorPowerOfTwo( image.height );\n\n var context = _canvas.getContext( '2d' );\n context.drawImage( image, 0, 0, _canvas.width, _canvas.height );\n\n console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image );\n\n return _canvas;\n\n }\n\n return image;\n\n }\n\n function textureNeedsPowerOfTwo( texture ) {\n\n return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||\n ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );\n\n }\n\n function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) {\n\n return texture.generateMipmaps && isPowerOfTwo &&\n texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;\n\n }\n\n function generateMipmap( target, texture, width, height ) {\n\n _gl.generateMipmap( target );\n\n var textureProperties = properties.get( texture );\n textureProperties.__maxMipLevel = Math.log2( Math.max( width, height ) );\n\n }\n\n // Fallback filters for non-power-of-2 textures\n\n function filterFallback( f ) {\n\n if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) {\n\n return _gl.NEAREST;\n\n }\n\n return _gl.LINEAR;\n\n }\n\n //\n\n function onTextureDispose( event ) {\n\n var texture = event.target;\n\n texture.removeEventListener( 'dispose', onTextureDispose );\n\n deallocateTexture( texture );\n\n if ( texture.isVideoTexture ) {\n\n delete _videoTextures[ texture.id ];\n\n }\n\n info.memory.textures --;\n\n }\n\n function onRenderTargetDispose( event ) {\n\n var renderTarget = event.target;\n\n renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );\n\n deallocateRenderTarget( renderTarget );\n\n info.memory.textures --;\n\n }\n\n //\n\n function deallocateTexture( texture ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.image && textureProperties.__image__webglTextureCube ) {\n\n // cube texture\n\n _gl.deleteTexture( textureProperties.__image__webglTextureCube );\n\n } else {\n\n // 2D texture\n\n if ( textureProperties.__webglInit === undefined ) return;\n\n _gl.deleteTexture( textureProperties.__webglTexture );\n\n }\n\n // remove all webgl properties\n properties.remove( texture );\n\n }\n\n function deallocateRenderTarget( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n var textureProperties = properties.get( renderTarget.texture );\n\n if ( ! renderTarget ) return;\n\n if ( textureProperties.__webglTexture !== undefined ) {\n\n _gl.deleteTexture( textureProperties.__webglTexture );\n\n }\n\n if ( renderTarget.depthTexture ) {\n\n renderTarget.depthTexture.dispose();\n\n }\n\n if ( renderTarget.isWebGLRenderTargetCube ) {\n\n for ( var i = 0; i < 6; i ++ ) {\n\n _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );\n if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );\n\n }\n\n } else {\n\n _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );\n if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );\n\n }\n\n properties.remove( renderTarget.texture );\n properties.remove( renderTarget );\n\n }\n\n //\n\n\n\n function setTexture2D( texture, slot ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.isVideoTexture ) updateVideoTexture( texture );\n\n if ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n var image = texture.image;\n\n if ( image === undefined ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );\n\n } else if ( image.complete === false ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );\n\n } else {\n\n uploadTexture( textureProperties, texture, slot );\n return;\n\n }\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n }\n\n function setTextureCube( texture, slot ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.image.length === 6 ) {\n\n if ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n if ( ! textureProperties.__image__webglTextureCube ) {\n\n texture.addEventListener( 'dispose', onTextureDispose );\n\n textureProperties.__image__webglTextureCube = _gl.createTexture();\n\n info.memory.textures ++;\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\n var isCompressed = ( texture && texture.isCompressedTexture );\n var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );\n\n var cubeImage = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( ! isCompressed && ! isDataTexture ) {\n\n cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );\n\n } else {\n\n cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];\n\n }\n\n }\n\n var image = cubeImage[ 0 ],\n isPowerOfTwoImage = isPowerOfTwo( image ),\n glFormat = utils.convert( texture.format ),\n glType = utils.convert( texture.type );\n\n setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage );\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( ! isCompressed ) {\n\n if ( isDataTexture ) {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );\n\n }\n\n } else {\n\n var mipmap, mipmaps = cubeImage[ i ].mipmaps;\n\n for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {\n\n mipmap = mipmaps[ j ];\n\n if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n } else {\n\n console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );\n\n }\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n }\n\n }\n\n }\n\n if ( ! isCompressed ) {\n\n textureProperties.__maxMipLevel = 0;\n\n } else {\n\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n }\n\n if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {\n\n // We assume images for cube map have the same size.\n generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height );\n\n }\n\n textureProperties.__version = texture.version;\n\n if ( texture.onUpdate ) texture.onUpdate( texture );\n\n } else {\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n }\n\n }\n\n }\n\n function setTextureCubeDynamic( texture, slot ) {\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );\n\n }\n\n function setTextureParameters( textureType, texture, isPowerOfTwoImage ) {\n\n var extension;\n\n if ( isPowerOfTwoImage ) {\n\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) );\n\n _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) );\n\n } else {\n\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );\n\n if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );\n\n }\n\n _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );\n\n if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );\n\n }\n\n }\n\n extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n if ( extension ) {\n\n if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;\n if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;\n\n if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {\n\n _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );\n properties.get( texture ).__currentAnisotropy = texture.anisotropy;\n\n }\n\n }\n\n }\n\n function uploadTexture( textureProperties, texture, slot ) {\n\n if ( textureProperties.__webglInit === undefined ) {\n\n textureProperties.__webglInit = true;\n\n texture.addEventListener( 'dispose', onTextureDispose );\n\n textureProperties.__webglTexture = _gl.createTexture();\n\n info.memory.textures ++;\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\n var image = clampToMaxSize( texture.image, capabilities.maxTextureSize );\n\n if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) {\n\n image = makePowerOfTwo( image );\n\n }\n\n var isPowerOfTwoImage = isPowerOfTwo( image ),\n glFormat = utils.convert( texture.format ),\n glType = utils.convert( texture.type );\n\n setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage );\n\n var mipmap, mipmaps = texture.mipmaps;\n\n if ( texture.isDepthTexture ) {\n\n // populate depth texture with dummy data\n\n var internalFormat = _gl.DEPTH_COMPONENT;\n\n if ( texture.type === FloatType ) {\n\n if ( ! _isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' );\n internalFormat = _gl.DEPTH_COMPONENT32F;\n\n } else if ( _isWebGL2 ) {\n\n // WebGL 2.0 requires signed internalformat for glTexImage2D\n internalFormat = _gl.DEPTH_COMPONENT16;\n\n }\n\n if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) {\n\n // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {\n\n console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );\n\n texture.type = UnsignedShortType;\n glType = utils.convert( texture.type );\n\n }\n\n }\n\n // Depth stencil textures need the DEPTH_STENCIL internal format\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.format === DepthStencilFormat ) {\n\n internalFormat = _gl.DEPTH_STENCIL;\n\n // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.type !== UnsignedInt248Type ) {\n\n console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );\n\n texture.type = UnsignedInt248Type;\n glType = utils.convert( texture.type );\n\n }\n\n }\n\n state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null );\n\n } else if ( texture.isDataTexture ) {\n\n // use manually created mipmaps if available\n // if there are no manual mipmaps\n // set 0 level mipmap and then use GL to generate other mipmap levels\n\n if ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n texture.generateMipmaps = false;\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );\n textureProperties.__maxMipLevel = 0;\n\n }\n\n } else if ( texture.isCompressedTexture ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n\n if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n } else {\n\n console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n }\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n }\n\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n // regular Texture (image, video, canvas)\n\n // use manually created mipmaps if available\n // if there are no manual mipmaps\n // set 0 level mipmap and then use GL to generate other mipmap levels\n\n if ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );\n\n }\n\n texture.generateMipmaps = false;\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image );\n textureProperties.__maxMipLevel = 0;\n\n }\n\n }\n\n if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {\n\n generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height );\n\n }\n\n textureProperties.__version = texture.version;\n\n if ( texture.onUpdate ) texture.onUpdate( texture );\n\n }\n\n // Render targets\n\n // Setup storage for target texture and bind it to correct framebuffer\n function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {\n\n var glFormat = utils.convert( renderTarget.texture.format );\n var glType = utils.convert( renderTarget.texture.type );\n state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n }\n\n // Setup storage for internal depth/stencil buffers and bind to correct framebuffer\n function setupRenderBufferStorage( renderbuffer, renderTarget ) {\n\n _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );\n\n if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {\n\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );\n _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {\n\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );\n _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n } else {\n\n // FIXME: We don't support !depth !stencil\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );\n\n }\n\n _gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n }\n\n // Setup resources for a Depth Texture for a FBO (needs an extension)\n function setupDepthTexture( framebuffer, renderTarget ) {\n\n var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube );\n if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {\n\n throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );\n\n }\n\n // upload an empty depth texture with framebuffer size\n if ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||\n renderTarget.depthTexture.image.width !== renderTarget.width ||\n renderTarget.depthTexture.image.height !== renderTarget.height ) {\n\n renderTarget.depthTexture.image.width = renderTarget.width;\n renderTarget.depthTexture.image.height = renderTarget.height;\n renderTarget.depthTexture.needsUpdate = true;\n\n }\n\n setTexture2D( renderTarget.depthTexture, 0 );\n\n var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;\n\n if ( renderTarget.depthTexture.format === DepthFormat ) {\n\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {\n\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n } else {\n\n throw new Error( 'Unknown depthTexture format' );\n\n }\n\n }\n\n // Setup GL resources for a non-texture depth buffer\n function setupDepthRenderbuffer( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n\n var isCube = ( renderTarget.isWebGLRenderTargetCube === true );\n\n if ( renderTarget.depthTexture ) {\n\n if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );\n\n setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );\n\n } else {\n\n if ( isCube ) {\n\n renderTargetProperties.__webglDepthbuffer = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );\n renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();\n setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );\n\n }\n\n } else {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();\n setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );\n\n }\n\n }\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n }\n\n // Set up GL resources for the render target\n function setupRenderTarget( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n var textureProperties = properties.get( renderTarget.texture );\n\n renderTarget.addEventListener( 'dispose', onRenderTargetDispose );\n\n textureProperties.__webglTexture = _gl.createTexture();\n\n info.memory.textures ++;\n\n var isCube = ( renderTarget.isWebGLRenderTargetCube === true );\n var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );\n\n // Setup framebuffer\n\n if ( isCube ) {\n\n renderTargetProperties.__webglFramebuffer = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();\n\n }\n\n } else {\n\n renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();\n\n }\n\n // Setup color buffer\n\n if ( isCube ) {\n\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );\n setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );\n\n for ( var i = 0; i < 6; i ++ ) {\n\n setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );\n\n }\n\n if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {\n\n generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height );\n\n }\n\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );\n\n } else {\n\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );\n setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D );\n\n if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {\n\n generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height );\n\n }\n\n state.bindTexture( _gl.TEXTURE_2D, null );\n\n }\n\n // Setup depth and stencil buffers\n\n if ( renderTarget.depthBuffer ) {\n\n setupDepthRenderbuffer( renderTarget );\n\n }\n\n }\n\n function updateRenderTargetMipmap( renderTarget ) {\n\n var texture = renderTarget.texture;\n var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );\n\n if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) {\n\n var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;\n var webglTexture = properties.get( texture ).__webglTexture;\n\n state.bindTexture( target, webglTexture );\n generateMipmap( target, texture, renderTarget.width, renderTarget.height );\n state.bindTexture( target, null );\n\n }\n\n }\n\n function updateVideoTexture( texture ) {\n\n var id = texture.id;\n var frame = info.render.frame;\n\n // Check the last frame we updated the VideoTexture\n\n if ( _videoTextures[ id ] !== frame ) {\n\n _videoTextures[ id ] = frame;\n texture.update();\n\n }\n\n }\n\n this.setTexture2D = setTexture2D;\n this.setTextureCube = setTextureCube;\n this.setTextureCubeDynamic = setTextureCubeDynamic;\n this.setupRenderTarget = setupRenderTarget;\n this.updateRenderTargetMipmap = updateRenderTargetMipmap;\n\n }\n\n /**\n * @author thespite / http://www.twitter.com/thespite\n */\n\n function WebGLUtils( gl, extensions ) {\n\n function convert( p ) {\n\n var extension;\n\n if ( p === RepeatWrapping ) return gl.REPEAT;\n if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE;\n if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT;\n\n if ( p === NearestFilter ) return gl.NEAREST;\n if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST;\n if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR;\n\n if ( p === LinearFilter ) return gl.LINEAR;\n if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST;\n if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR;\n\n if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;\n if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;\n if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;\n if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5;\n\n if ( p === ByteType ) return gl.BYTE;\n if ( p === ShortType ) return gl.SHORT;\n if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;\n if ( p === IntType ) return gl.INT;\n if ( p === UnsignedIntType ) return gl.UNSIGNED_INT;\n if ( p === FloatType ) return gl.FLOAT;\n\n if ( p === HalfFloatType ) {\n\n extension = extensions.get( 'OES_texture_half_float' );\n\n if ( extension !== null ) return extension.HALF_FLOAT_OES;\n\n }\n\n if ( p === AlphaFormat ) return gl.ALPHA;\n if ( p === RGBFormat ) return gl.RGB;\n if ( p === RGBAFormat ) return gl.RGBA;\n if ( p === LuminanceFormat ) return gl.LUMINANCE;\n if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;\n if ( p === DepthFormat ) return gl.DEPTH_COMPONENT;\n if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;\n\n if ( p === AddEquation ) return gl.FUNC_ADD;\n if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT;\n if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT;\n\n if ( p === ZeroFactor ) return gl.ZERO;\n if ( p === OneFactor ) return gl.ONE;\n if ( p === SrcColorFactor ) return gl.SRC_COLOR;\n if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR;\n if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA;\n if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA;\n if ( p === DstAlphaFactor ) return gl.DST_ALPHA;\n if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA;\n\n if ( p === DstColorFactor ) return gl.DST_COLOR;\n if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR;\n if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE;\n\n if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format ||\n p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n if ( extension !== null ) {\n\n if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;\n if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;\n if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;\n if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\n }\n\n }\n\n if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||\n p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n if ( extension !== null ) {\n\n if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;\n if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;\n if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;\n if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;\n\n }\n\n }\n\n if ( p === RGB_ETC1_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_etc1' );\n\n if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL;\n\n }\n\n if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||\n p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||\n p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||\n p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||\n p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_astc' );\n\n if ( extension !== null ) {\n\n return p;\n\n }\n\n }\n\n if ( p === MinEquation || p === MaxEquation ) {\n\n extension = extensions.get( 'EXT_blend_minmax' );\n\n if ( extension !== null ) {\n\n if ( p === MinEquation ) return extension.MIN_EXT;\n if ( p === MaxEquation ) return extension.MAX_EXT;\n\n }\n\n }\n\n if ( p === UnsignedInt248Type ) {\n\n extension = extensions.get( 'WEBGL_depth_texture' );\n\n if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL;\n\n }\n\n return 0;\n\n }\n\n return { convert: convert };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author greggman / http://games.greggman.com/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author tschw\n */\n\n function PerspectiveCamera( fov, aspect, near, far ) {\n\n Camera.call( this );\n\n this.type = 'PerspectiveCamera';\n\n this.fov = fov !== undefined ? fov : 50;\n this.zoom = 1;\n\n this.near = near !== undefined ? near : 0.1;\n this.far = far !== undefined ? far : 2000;\n this.focus = 10;\n\n this.aspect = aspect !== undefined ? aspect : 1;\n this.view = null;\n\n this.filmGauge = 35; // width of the film (default in millimeters)\n this.filmOffset = 0; // horizontal film offset (same unit as gauge)\n\n this.updateProjectionMatrix();\n\n }\n\n PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n constructor: PerspectiveCamera,\n\n isPerspectiveCamera: true,\n\n copy: function ( source, recursive ) {\n\n Camera.prototype.copy.call( this, source, recursive );\n\n this.fov = source.fov;\n this.zoom = source.zoom;\n\n this.near = source.near;\n this.far = source.far;\n this.focus = source.focus;\n\n this.aspect = source.aspect;\n this.view = source.view === null ? null : Object.assign( {}, source.view );\n\n this.filmGauge = source.filmGauge;\n this.filmOffset = source.filmOffset;\n\n return this;\n\n },\n\n /**\n * Sets the FOV by focal length in respect to the current .filmGauge.\n *\n * The default film gauge is 35, so that the focal length can be specified for\n * a 35mm (full frame) camera.\n *\n * Values for focal length and film gauge must have the same unit.\n */\n setFocalLength: function ( focalLength ) {\n\n // see http://www.bobatkins.com/photography/technical/field_of_view.html\n var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;\n\n this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );\n this.updateProjectionMatrix();\n\n },\n\n /**\n * Calculates the focal length from the current .fov and .filmGauge.\n */\n getFocalLength: function () {\n\n var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );\n\n return 0.5 * this.getFilmHeight() / vExtentSlope;\n\n },\n\n getEffectiveFOV: function () {\n\n return _Math.RAD2DEG * 2 * Math.atan(\n Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );\n\n },\n\n getFilmWidth: function () {\n\n // film not completely covered in portrait format (aspect < 1)\n return this.filmGauge * Math.min( this.aspect, 1 );\n\n },\n\n getFilmHeight: function () {\n\n // film not completely covered in landscape format (aspect > 1)\n return this.filmGauge / Math.max( this.aspect, 1 );\n\n },\n\n /**\n * Sets an offset in a larger frustum. This is useful for multi-window or\n * multi-monitor/multi-machine setups.\n *\n * For example, if you have 3x2 monitors and each monitor is 1920x1080 and\n * the monitors are in grid like this\n *\n * +---+---+---+\n * | A | B | C |\n * +---+---+---+\n * | D | E | F |\n * +---+---+---+\n *\n * then for each monitor you would call it like this\n *\n * var w = 1920;\n * var h = 1080;\n * var fullWidth = w * 3;\n * var fullHeight = h * 2;\n *\n * --A--\n * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );\n * --B--\n * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );\n * --C--\n * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );\n * --D--\n * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );\n * --E--\n * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );\n * --F--\n * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );\n *\n * Note there is no reason monitors have to be the same size or in a grid.\n */\n setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {\n\n this.aspect = fullWidth / fullHeight;\n\n if ( this.view === null ) {\n\n this.view = {\n enabled: true,\n fullWidth: 1,\n fullHeight: 1,\n offsetX: 0,\n offsetY: 0,\n width: 1,\n height: 1\n };\n\n }\n\n this.view.enabled = true;\n this.view.fullWidth = fullWidth;\n this.view.fullHeight = fullHeight;\n this.view.offsetX = x;\n this.view.offsetY = y;\n this.view.width = width;\n this.view.height = height;\n\n this.updateProjectionMatrix();\n\n },\n\n clearViewOffset: function () {\n\n if ( this.view !== null ) {\n\n this.view.enabled = false;\n\n }\n\n this.updateProjectionMatrix();\n\n },\n\n updateProjectionMatrix: function () {\n\n var near = this.near,\n top = near * Math.tan(\n _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,\n height = 2 * top,\n width = this.aspect * height,\n left = - 0.5 * width,\n view = this.view;\n\n if ( this.view !== null && this.view.enabled ) {\n\n var fullWidth = view.fullWidth,\n fullHeight = view.fullHeight;\n\n left += view.offsetX * width / fullWidth;\n top -= view.offsetY * height / fullHeight;\n width *= view.width / fullWidth;\n height *= view.height / fullHeight;\n\n }\n\n var skew = this.filmOffset;\n if ( skew !== 0 ) left += near * skew / this.getFilmWidth();\n\n this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.fov = this.fov;\n data.object.zoom = this.zoom;\n\n data.object.near = this.near;\n data.object.far = this.far;\n data.object.focus = this.focus;\n\n data.object.aspect = this.aspect;\n\n if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n data.object.filmGauge = this.filmGauge;\n data.object.filmOffset = this.filmOffset;\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ArrayCamera( array ) {\n\n PerspectiveCamera.call( this );\n\n this.cameras = array || [];\n\n }\n\n ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), {\n\n constructor: ArrayCamera,\n\n isArrayCamera: true\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebVRManager( renderer ) {\n\n var scope = this;\n\n var device = null;\n var frameData = null;\n\n var poseTarget = null;\n\n var standingMatrix = new Matrix4();\n var standingMatrixInverse = new Matrix4();\n\n if ( typeof window !== 'undefined' && 'VRFrameData' in window ) {\n\n frameData = new window.VRFrameData();\n\n }\n\n var matrixWorldInverse = new Matrix4();\n var tempQuaternion = new Quaternion();\n var tempPosition = new Vector3();\n\n var cameraL = new PerspectiveCamera();\n cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );\n cameraL.layers.enable( 1 );\n\n var cameraR = new PerspectiveCamera();\n cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );\n cameraR.layers.enable( 2 );\n\n var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );\n cameraVR.layers.enable( 1 );\n cameraVR.layers.enable( 2 );\n\n //\n\n var currentSize, currentPixelRatio;\n\n function onVRDisplayPresentChange() {\n\n if ( device !== null && device.isPresenting ) {\n\n var eyeParameters = device.getEyeParameters( 'left' );\n var renderWidth = eyeParameters.renderWidth;\n var renderHeight = eyeParameters.renderHeight;\n\n currentPixelRatio = renderer.getPixelRatio();\n currentSize = renderer.getSize();\n\n renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );\n\n } else if ( scope.enabled ) {\n\n renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio );\n\n }\n\n }\n\n if ( typeof window !== 'undefined' ) {\n\n window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );\n\n }\n\n //\n\n this.enabled = false;\n this.userHeight = 1.6;\n\n this.getDevice = function () {\n\n return device;\n\n };\n\n this.setDevice = function ( value ) {\n\n if ( value !== undefined ) device = value;\n\n };\n\n this.setPoseTarget = function ( object ) {\n\n if ( object !== undefined ) poseTarget = object;\n\n };\n\n this.getCamera = function ( camera ) {\n\n if ( device === null ) return camera;\n\n device.depthNear = camera.near;\n device.depthFar = camera.far;\n\n device.getFrameData( frameData );\n\n //\n\n var stageParameters = device.stageParameters;\n\n if ( stageParameters ) {\n\n standingMatrix.fromArray( stageParameters.sittingToStandingTransform );\n\n } else {\n\n standingMatrix.makeTranslation( 0, scope.userHeight, 0 );\n\n }\n\n\n var pose = frameData.pose;\n var poseObject = poseTarget !== null ? poseTarget : camera;\n\n // We want to manipulate poseObject by its position and quaternion components since users may rely on them.\n poseObject.matrix.copy( standingMatrix );\n poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale );\n\n if ( pose.orientation !== null ) {\n\n tempQuaternion.fromArray( pose.orientation );\n poseObject.quaternion.multiply( tempQuaternion );\n\n }\n\n if ( pose.position !== null ) {\n\n tempQuaternion.setFromRotationMatrix( standingMatrix );\n tempPosition.fromArray( pose.position );\n tempPosition.applyQuaternion( tempQuaternion );\n poseObject.position.add( tempPosition );\n\n }\n\n poseObject.updateMatrixWorld();\n\n if ( device.isPresenting === false ) return camera;\n\n //\n\n cameraL.near = camera.near;\n cameraR.near = camera.near;\n\n cameraL.far = camera.far;\n cameraR.far = camera.far;\n\n cameraVR.matrixWorld.copy( camera.matrixWorld );\n cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );\n\n cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix );\n cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix );\n\n // TODO (mrdoob) Double check this code\n\n standingMatrixInverse.getInverse( standingMatrix );\n\n cameraL.matrixWorldInverse.multiply( standingMatrixInverse );\n cameraR.matrixWorldInverse.multiply( standingMatrixInverse );\n\n var parent = poseObject.parent;\n\n if ( parent !== null ) {\n\n matrixWorldInverse.getInverse( parent.matrixWorld );\n\n cameraL.matrixWorldInverse.multiply( matrixWorldInverse );\n cameraR.matrixWorldInverse.multiply( matrixWorldInverse );\n\n }\n\n // envMap and Mirror needs camera.matrixWorld\n\n cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse );\n cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse );\n\n cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix );\n cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix );\n\n // HACK (mrdoob)\n // https://github.com/w3c/webvr/issues/203\n\n cameraVR.projectionMatrix.copy( cameraL.projectionMatrix );\n\n //\n\n var layers = device.getLayers();\n\n if ( layers.length ) {\n\n var layer = layers[ 0 ];\n\n if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) {\n\n cameraL.bounds.fromArray( layer.leftBounds );\n\n }\n\n if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) {\n\n cameraR.bounds.fromArray( layer.rightBounds );\n\n }\n\n }\n\n return cameraVR;\n\n };\n\n this.getStandingMatrix = function () {\n\n return standingMatrix;\n\n };\n\n this.submitFrame = function () {\n\n if ( device && device.isPresenting ) device.submitFrame();\n\n };\n\n this.dispose = function () {\n\n if ( typeof window !== 'undefined' ) {\n\n window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange );\n\n }\n\n };\n\n }\n\n /**\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author szimek / https://github.com/szimek/\n * @author tschw\n */\n\n function WebGLRenderer( parameters ) {\n\n console.log( 'THREE.WebGLRenderer', REVISION );\n\n parameters = parameters || {};\n\n var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),\n _context = parameters.context !== undefined ? parameters.context : null,\n\n _alpha = parameters.alpha !== undefined ? parameters.alpha : false,\n _depth = parameters.depth !== undefined ? parameters.depth : true,\n _stencil = parameters.stencil !== undefined ? parameters.stencil : true,\n _antialias = parameters.antialias !== undefined ? parameters.antialias : false,\n _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,\n _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,\n _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default';\n\n var currentRenderList = null;\n var currentRenderState = null;\n\n // public properties\n\n this.domElement = _canvas;\n this.context = null;\n\n // clearing\n\n this.autoClear = true;\n this.autoClearColor = true;\n this.autoClearDepth = true;\n this.autoClearStencil = true;\n\n // scene graph\n\n this.sortObjects = true;\n\n // user-defined clipping\n\n this.clippingPlanes = [];\n this.localClippingEnabled = false;\n\n // physically based shading\n\n this.gammaFactor = 2.0; // for backwards compatibility\n this.gammaInput = false;\n this.gammaOutput = false;\n\n // physical lights\n\n this.physicallyCorrectLights = false;\n\n // tone mapping\n\n this.toneMapping = LinearToneMapping;\n this.toneMappingExposure = 1.0;\n this.toneMappingWhitePoint = 1.0;\n\n // morphs\n\n this.maxMorphTargets = 8;\n this.maxMorphNormals = 4;\n\n // internal properties\n\n var _this = this,\n\n _isContextLost = false,\n\n // internal state cache\n\n _currentRenderTarget = null,\n _currentFramebuffer = null,\n _currentMaterialId = - 1,\n _currentGeometryProgram = '',\n\n _currentCamera = null,\n _currentArrayCamera = null,\n\n _currentViewport = new Vector4(),\n _currentScissor = new Vector4(),\n _currentScissorTest = null,\n\n //\n\n _usedTextureUnits = 0,\n\n //\n\n _width = _canvas.width,\n _height = _canvas.height,\n\n _pixelRatio = 1,\n\n _viewport = new Vector4( 0, 0, _width, _height ),\n _scissor = new Vector4( 0, 0, _width, _height ),\n _scissorTest = false,\n\n // frustum\n\n _frustum = new Frustum(),\n\n // clipping\n\n _clipping = new WebGLClipping(),\n _clippingEnabled = false,\n _localClippingEnabled = false,\n\n // camera matrices cache\n\n _projScreenMatrix = new Matrix4(),\n\n _vector3 = new Vector3();\n\n function getTargetPixelRatio() {\n\n return _currentRenderTarget === null ? _pixelRatio : 1;\n\n }\n\n // initialize\n\n var _gl;\n\n try {\n\n var contextAttributes = {\n alpha: _alpha,\n depth: _depth,\n stencil: _stencil,\n antialias: _antialias,\n premultipliedAlpha: _premultipliedAlpha,\n preserveDrawingBuffer: _preserveDrawingBuffer,\n powerPreference: _powerPreference\n };\n\n // event listeners must be registered before WebGL context is created, see #12753\n\n _canvas.addEventListener( 'webglcontextlost', onContextLost, false );\n _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );\n\n _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );\n\n if ( _gl === null ) {\n\n if ( _canvas.getContext( 'webgl' ) !== null ) {\n\n throw new Error( 'Error creating WebGL context with your selected attributes.' );\n\n } else {\n\n throw new Error( 'Error creating WebGL context.' );\n\n }\n\n }\n\n // Some experimental-webgl implementations do not have getShaderPrecisionFormat\n\n if ( _gl.getShaderPrecisionFormat === undefined ) {\n\n _gl.getShaderPrecisionFormat = function () {\n\n return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };\n\n };\n\n }\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLRenderer: ' + error.message );\n\n }\n\n var extensions, capabilities, state, info;\n var properties, textures, attributes, geometries, objects;\n var programCache, renderLists, renderStates;\n\n var background, morphtargets, bufferRenderer, indexedBufferRenderer;\n var spriteRenderer;\n\n var utils;\n\n function initGLContext() {\n\n extensions = new WebGLExtensions( _gl );\n extensions.get( 'WEBGL_depth_texture' );\n extensions.get( 'OES_texture_float' );\n extensions.get( 'OES_texture_float_linear' );\n extensions.get( 'OES_texture_half_float' );\n extensions.get( 'OES_texture_half_float_linear' );\n extensions.get( 'OES_standard_derivatives' );\n extensions.get( 'OES_element_index_uint' );\n extensions.get( 'ANGLE_instanced_arrays' );\n\n utils = new WebGLUtils( _gl, extensions );\n\n capabilities = new WebGLCapabilities( _gl, extensions, parameters );\n\n state = new WebGLState( _gl, extensions, utils );\n state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );\n state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );\n\n info = new WebGLInfo( _gl );\n properties = new WebGLProperties();\n textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );\n attributes = new WebGLAttributes( _gl );\n geometries = new WebGLGeometries( _gl, attributes, info );\n objects = new WebGLObjects( geometries, info );\n morphtargets = new WebGLMorphtargets( _gl );\n programCache = new WebGLPrograms( _this, extensions, capabilities );\n renderLists = new WebGLRenderLists();\n renderStates = new WebGLRenderStates();\n\n background = new WebGLBackground( _this, state, geometries, _premultipliedAlpha );\n\n bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info );\n indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info );\n\n spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities );\n\n info.programs = programCache.programs;\n\n _this.context = _gl;\n _this.capabilities = capabilities;\n _this.extensions = extensions;\n _this.properties = properties;\n _this.renderLists = renderLists;\n _this.state = state;\n _this.info = info;\n\n }\n\n initGLContext();\n\n // vr\n\n var vr = new WebVRManager( _this );\n\n this.vr = vr;\n\n // shadow map\n\n var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );\n\n this.shadowMap = shadowMap;\n\n // API\n\n this.getContext = function () {\n\n return _gl;\n\n };\n\n this.getContextAttributes = function () {\n\n return _gl.getContextAttributes();\n\n };\n\n this.forceContextLoss = function () {\n\n var extension = extensions.get( 'WEBGL_lose_context' );\n if ( extension ) extension.loseContext();\n\n };\n\n this.forceContextRestore = function () {\n\n var extension = extensions.get( 'WEBGL_lose_context' );\n if ( extension ) extension.restoreContext();\n\n };\n\n this.getPixelRatio = function () {\n\n return _pixelRatio;\n\n };\n\n this.setPixelRatio = function ( value ) {\n\n if ( value === undefined ) return;\n\n _pixelRatio = value;\n\n this.setSize( _width, _height, false );\n\n };\n\n this.getSize = function () {\n\n return {\n width: _width,\n height: _height\n };\n\n };\n\n this.setSize = function ( width, height, updateStyle ) {\n\n var device = vr.getDevice();\n\n if ( device && device.isPresenting ) {\n\n console.warn( 'THREE.WebGLRenderer: Can\\'t change size while VR device is presenting.' );\n return;\n\n }\n\n _width = width;\n _height = height;\n\n _canvas.width = width * _pixelRatio;\n _canvas.height = height * _pixelRatio;\n\n if ( updateStyle !== false ) {\n\n _canvas.style.width = width + 'px';\n _canvas.style.height = height + 'px';\n\n }\n\n this.setViewport( 0, 0, width, height );\n\n };\n\n this.getDrawingBufferSize = function () {\n\n return {\n width: _width * _pixelRatio,\n height: _height * _pixelRatio\n };\n\n };\n\n this.setDrawingBufferSize = function ( width, height, pixelRatio ) {\n\n _width = width;\n _height = height;\n\n _pixelRatio = pixelRatio;\n\n _canvas.width = width * pixelRatio;\n _canvas.height = height * pixelRatio;\n\n this.setViewport( 0, 0, width, height );\n\n };\n\n this.getCurrentViewport = function () {\n\n return _currentViewport;\n\n };\n\n this.setViewport = function ( x, y, width, height ) {\n\n _viewport.set( x, _height - y - height, width, height );\n state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );\n\n };\n\n this.setScissor = function ( x, y, width, height ) {\n\n _scissor.set( x, _height - y - height, width, height );\n state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );\n\n };\n\n this.setScissorTest = function ( boolean ) {\n\n state.setScissorTest( _scissorTest = boolean );\n\n };\n\n // Clearing\n\n this.getClearColor = function () {\n\n return background.getClearColor();\n\n };\n\n this.setClearColor = function () {\n\n background.setClearColor.apply( background, arguments );\n\n };\n\n this.getClearAlpha = function () {\n\n return background.getClearAlpha();\n\n };\n\n this.setClearAlpha = function () {\n\n background.setClearAlpha.apply( background, arguments );\n\n };\n\n this.clear = function ( color, depth, stencil ) {\n\n var bits = 0;\n\n if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;\n if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;\n if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;\n\n _gl.clear( bits );\n\n };\n\n this.clearColor = function () {\n\n this.clear( true, false, false );\n\n };\n\n this.clearDepth = function () {\n\n this.clear( false, true, false );\n\n };\n\n this.clearStencil = function () {\n\n this.clear( false, false, true );\n\n };\n\n this.clearTarget = function ( renderTarget, color, depth, stencil ) {\n\n this.setRenderTarget( renderTarget );\n this.clear( color, depth, stencil );\n\n };\n\n //\n\n this.dispose = function () {\n\n _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );\n _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );\n\n renderLists.dispose();\n renderStates.dispose();\n properties.dispose();\n objects.dispose();\n\n vr.dispose();\n\n stopAnimation();\n\n };\n\n // Events\n\n function onContextLost( event ) {\n\n event.preventDefault();\n\n console.log( 'THREE.WebGLRenderer: Context Lost.' );\n\n _isContextLost = true;\n\n }\n\n function onContextRestore( /* event */ ) {\n\n console.log( 'THREE.WebGLRenderer: Context Restored.' );\n\n _isContextLost = false;\n\n initGLContext();\n\n }\n\n function onMaterialDispose( event ) {\n\n var material = event.target;\n\n material.removeEventListener( 'dispose', onMaterialDispose );\n\n deallocateMaterial( material );\n\n }\n\n // Buffer deallocation\n\n function deallocateMaterial( material ) {\n\n releaseMaterialProgramReference( material );\n\n properties.remove( material );\n\n }\n\n\n function releaseMaterialProgramReference( material ) {\n\n var programInfo = properties.get( material ).program;\n\n material.program = undefined;\n\n if ( programInfo !== undefined ) {\n\n programCache.releaseProgram( programInfo );\n\n }\n\n }\n\n // Buffer rendering\n\n function renderObjectImmediate( object, program, material ) {\n\n object.render( function ( object ) {\n\n _this.renderBufferImmediate( object, program, material );\n\n } );\n\n }\n\n this.renderBufferImmediate = function ( object, program, material ) {\n\n state.initAttributes();\n\n var buffers = properties.get( object );\n\n if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();\n if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();\n if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();\n if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();\n\n var programAttributes = program.getAttributes();\n\n if ( object.hasPositions ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.position );\n _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasNormals ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );\n\n if ( ! material.isMeshPhongMaterial &&\n ! material.isMeshStandardMaterial &&\n ! material.isMeshNormalMaterial &&\n material.flatShading === true ) {\n\n for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {\n\n var array = object.normalArray;\n\n var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;\n var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;\n var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;\n\n array[ i + 0 ] = nx;\n array[ i + 1 ] = ny;\n array[ i + 2 ] = nz;\n\n array[ i + 3 ] = nx;\n array[ i + 4 ] = ny;\n array[ i + 5 ] = nz;\n\n array[ i + 6 ] = nx;\n array[ i + 7 ] = ny;\n array[ i + 8 ] = nz;\n\n }\n\n }\n\n _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.normal );\n\n _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasUvs && material.map ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.uv );\n\n _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasColors && material.vertexColors !== NoColors ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.color );\n\n _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n state.disableUnusedAttributes();\n\n _gl.drawArrays( _gl.TRIANGLES, 0, object.count );\n\n object.count = 0;\n\n };\n\n this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {\n\n var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n state.setMaterial( material, frontFaceCW );\n\n var program = setProgram( camera, fog, material, object );\n var geometryProgram = geometry.id + '_' + program.id + '_' + ( material.wireframe === true );\n\n var updateBuffers = false;\n\n if ( geometryProgram !== _currentGeometryProgram ) {\n\n _currentGeometryProgram = geometryProgram;\n updateBuffers = true;\n\n }\n\n if ( object.morphTargetInfluences ) {\n\n morphtargets.update( object, geometry, material, program );\n\n updateBuffers = true;\n\n }\n\n //\n\n var index = geometry.index;\n var position = geometry.attributes.position;\n var rangeFactor = 1;\n\n if ( material.wireframe === true ) {\n\n index = geometries.getWireframeAttribute( geometry );\n rangeFactor = 2;\n\n }\n\n var attribute;\n var renderer = bufferRenderer;\n\n if ( index !== null ) {\n\n attribute = attributes.get( index );\n\n renderer = indexedBufferRenderer;\n renderer.setIndex( attribute );\n\n }\n\n if ( updateBuffers ) {\n\n setupVertexAttributes( material, program, geometry );\n\n if ( index !== null ) {\n\n _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer );\n\n }\n\n }\n\n //\n\n var dataCount = Infinity;\n\n if ( index !== null ) {\n\n dataCount = index.count;\n\n } else if ( position !== undefined ) {\n\n dataCount = position.count;\n\n }\n\n var rangeStart = geometry.drawRange.start * rangeFactor;\n var rangeCount = geometry.drawRange.count * rangeFactor;\n\n var groupStart = group !== null ? group.start * rangeFactor : 0;\n var groupCount = group !== null ? group.count * rangeFactor : Infinity;\n\n var drawStart = Math.max( rangeStart, groupStart );\n var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;\n\n var drawCount = Math.max( 0, drawEnd - drawStart + 1 );\n\n if ( drawCount === 0 ) return;\n\n //\n\n if ( object.isMesh ) {\n\n if ( material.wireframe === true ) {\n\n state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );\n renderer.setMode( _gl.LINES );\n\n } else {\n\n switch ( object.drawMode ) {\n\n case TrianglesDrawMode:\n renderer.setMode( _gl.TRIANGLES );\n break;\n\n case TriangleStripDrawMode:\n renderer.setMode( _gl.TRIANGLE_STRIP );\n break;\n\n case TriangleFanDrawMode:\n renderer.setMode( _gl.TRIANGLE_FAN );\n break;\n\n }\n\n }\n\n\n } else if ( object.isLine ) {\n\n var lineWidth = material.linewidth;\n\n if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material\n\n state.setLineWidth( lineWidth * getTargetPixelRatio() );\n\n if ( object.isLineSegments ) {\n\n renderer.setMode( _gl.LINES );\n\n } else if ( object.isLineLoop ) {\n\n renderer.setMode( _gl.LINE_LOOP );\n\n } else {\n\n renderer.setMode( _gl.LINE_STRIP );\n\n }\n\n } else if ( object.isPoints ) {\n\n renderer.setMode( _gl.POINTS );\n\n }\n\n if ( geometry && geometry.isInstancedBufferGeometry ) {\n\n if ( geometry.maxInstancedCount > 0 ) {\n\n renderer.renderInstances( geometry, drawStart, drawCount );\n\n }\n\n } else {\n\n renderer.render( drawStart, drawCount );\n\n }\n\n };\n\n function setupVertexAttributes( material, program, geometry, startIndex ) {\n\n if ( geometry && geometry.isInstancedBufferGeometry ) {\n\n if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {\n\n console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n }\n\n if ( startIndex === undefined ) startIndex = 0;\n\n state.initAttributes();\n\n var geometryAttributes = geometry.attributes;\n\n var programAttributes = program.getAttributes();\n\n var materialDefaultAttributeValues = material.defaultAttributeValues;\n\n for ( var name in programAttributes ) {\n\n var programAttribute = programAttributes[ name ];\n\n if ( programAttribute >= 0 ) {\n\n var geometryAttribute = geometryAttributes[ name ];\n\n if ( geometryAttribute !== undefined ) {\n\n var normalized = geometryAttribute.normalized;\n var size = geometryAttribute.itemSize;\n\n var attribute = attributes.get( geometryAttribute );\n\n // TODO Attribute may not be available on context restore\n\n if ( attribute === undefined ) continue;\n\n var buffer = attribute.buffer;\n var type = attribute.type;\n var bytesPerElement = attribute.bytesPerElement;\n\n if ( geometryAttribute.isInterleavedBufferAttribute ) {\n\n var data = geometryAttribute.data;\n var stride = data.stride;\n var offset = geometryAttribute.offset;\n\n if ( data && data.isInstancedInterleavedBuffer ) {\n\n state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );\n\n if ( geometry.maxInstancedCount === undefined ) {\n\n geometry.maxInstancedCount = data.meshPerAttribute * data.count;\n\n }\n\n } else {\n\n state.enableAttribute( programAttribute );\n\n }\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement );\n\n } else {\n\n if ( geometryAttribute.isInstancedBufferAttribute ) {\n\n state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );\n\n if ( geometry.maxInstancedCount === undefined ) {\n\n geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;\n\n }\n\n } else {\n\n state.enableAttribute( programAttribute );\n\n }\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement );\n\n }\n\n } else if ( materialDefaultAttributeValues !== undefined ) {\n\n var value = materialDefaultAttributeValues[ name ];\n\n if ( value !== undefined ) {\n\n switch ( value.length ) {\n\n case 2:\n _gl.vertexAttrib2fv( programAttribute, value );\n break;\n\n case 3:\n _gl.vertexAttrib3fv( programAttribute, value );\n break;\n\n case 4:\n _gl.vertexAttrib4fv( programAttribute, value );\n break;\n\n default:\n _gl.vertexAttrib1fv( programAttribute, value );\n\n }\n\n }\n\n }\n\n }\n\n }\n\n state.disableUnusedAttributes();\n\n }\n\n // Compile\n\n this.compile = function ( scene, camera ) {\n\n currentRenderState = renderStates.get( scene, camera );\n currentRenderState.init();\n\n scene.traverse( function ( object ) {\n\n if ( object.isLight ) {\n\n currentRenderState.pushLight( object );\n\n if ( object.castShadow ) {\n\n currentRenderState.pushShadow( object );\n\n }\n\n }\n\n } );\n\n currentRenderState.setupLights( camera );\n\n scene.traverse( function ( object ) {\n\n if ( object.material ) {\n\n if ( Array.isArray( object.material ) ) {\n\n for ( var i = 0; i < object.material.length; i ++ ) {\n\n initMaterial( object.material[ i ], scene.fog, object );\n\n }\n\n } else {\n\n initMaterial( object.material, scene.fog, object );\n\n }\n\n }\n\n } );\n\n };\n\n // Animation Loop\n\n var isAnimating = false;\n var onAnimationFrame = null;\n\n function startAnimation() {\n\n if ( isAnimating ) return;\n\n requestAnimationLoopFrame();\n\n isAnimating = true;\n\n }\n\n function stopAnimation() {\n\n isAnimating = false;\n\n }\n\n function requestAnimationLoopFrame() {\n\n var device = vr.getDevice();\n\n if ( device && device.isPresenting ) {\n\n device.requestAnimationFrame( animationLoop );\n\n } else {\n\n window.requestAnimationFrame( animationLoop );\n\n }\n\n }\n\n function animationLoop( time ) {\n\n if ( isAnimating === false ) return;\n\n onAnimationFrame( time );\n\n requestAnimationLoopFrame();\n\n }\n\n this.animate = function ( callback ) {\n\n onAnimationFrame = callback;\n onAnimationFrame !== null ? startAnimation() : stopAnimation();\n\n };\n\n // Rendering\n\n this.render = function ( scene, camera, renderTarget, forceClear ) {\n\n if ( ! ( camera && camera.isCamera ) ) {\n\n console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );\n return;\n\n }\n\n if ( _isContextLost ) return;\n\n // reset caching for this frame\n\n _currentGeometryProgram = '';\n _currentMaterialId = - 1;\n _currentCamera = null;\n\n // update scene graph\n\n if ( scene.autoUpdate === true ) scene.updateMatrixWorld();\n\n // update camera matrices and frustum\n\n if ( camera.parent === null ) camera.updateMatrixWorld();\n\n if ( vr.enabled ) {\n\n camera = vr.getCamera( camera );\n\n }\n\n //\n\n currentRenderState = renderStates.get( scene, camera );\n currentRenderState.init();\n\n scene.onBeforeRender( _this, scene, camera, renderTarget );\n\n _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n _frustum.setFromMatrix( _projScreenMatrix );\n\n _localClippingEnabled = this.localClippingEnabled;\n _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );\n\n currentRenderList = renderLists.get( scene, camera );\n currentRenderList.init();\n\n projectObject( scene, camera, _this.sortObjects );\n\n if ( _this.sortObjects === true ) {\n\n currentRenderList.sort();\n\n }\n\n //\n\n if ( _clippingEnabled ) _clipping.beginShadows();\n\n var shadowsArray = currentRenderState.state.shadowsArray;\n\n shadowMap.render( shadowsArray, scene, camera );\n\n currentRenderState.setupLights( camera );\n\n if ( _clippingEnabled ) _clipping.endShadows();\n\n //\n\n if ( this.info.autoReset ) this.info.reset();\n\n if ( renderTarget === undefined ) {\n\n renderTarget = null;\n\n }\n\n this.setRenderTarget( renderTarget );\n\n //\n\n background.render( currentRenderList, scene, camera, forceClear );\n\n // render scene\n\n var opaqueObjects = currentRenderList.opaque;\n var transparentObjects = currentRenderList.transparent;\n\n if ( scene.overrideMaterial ) {\n\n var overrideMaterial = scene.overrideMaterial;\n\n if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );\n if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );\n\n } else {\n\n // opaque pass (front-to-back order)\n\n if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );\n\n // transparent pass (back-to-front order)\n\n if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );\n\n }\n\n // custom renderers\n\n var spritesArray = currentRenderState.state.spritesArray;\n\n spriteRenderer.render( spritesArray, scene, camera );\n\n // Generate mipmap if we're using any kind of mipmap filtering\n\n if ( renderTarget ) {\n\n textures.updateRenderTargetMipmap( renderTarget );\n\n }\n\n // Ensure depth buffer writing is enabled so it can be cleared on next render\n\n state.buffers.depth.setTest( true );\n state.buffers.depth.setMask( true );\n state.buffers.color.setMask( true );\n\n state.setPolygonOffset( false );\n\n scene.onAfterRender( _this, scene, camera );\n\n if ( vr.enabled ) {\n\n vr.submitFrame();\n\n }\n\n // _gl.finish();\n\n currentRenderList = null;\n currentRenderState = null;\n\n };\n\n /*\n // TODO Duplicated code (Frustum)\n\n var _sphere = new Sphere();\n\n function isObjectViewable( object ) {\n\n var geometry = object.geometry;\n\n if ( geometry.boundingSphere === null )\n geometry.computeBoundingSphere();\n\n _sphere.copy( geometry.boundingSphere ).\n applyMatrix4( object.matrixWorld );\n\n return isSphereViewable( _sphere );\n\n }\n\n function isSpriteViewable( sprite ) {\n\n _sphere.center.set( 0, 0, 0 );\n _sphere.radius = 0.7071067811865476;\n _sphere.applyMatrix4( sprite.matrixWorld );\n\n return isSphereViewable( _sphere );\n\n }\n\n function isSphereViewable( sphere ) {\n\n if ( ! _frustum.intersectsSphere( sphere ) ) return false;\n\n var numPlanes = _clipping.numPlanes;\n\n if ( numPlanes === 0 ) return true;\n\n var planes = _this.clippingPlanes,\n\n center = sphere.center,\n negRad = - sphere.radius,\n i = 0;\n\n do {\n\n // out when deeper than radius in the negative halfspace\n if ( planes[ i ].distanceToPoint( center ) < negRad ) return false;\n\n } while ( ++ i !== numPlanes );\n\n return true;\n\n }\n */\n\n function projectObject( object, camera, sortObjects ) {\n\n if ( object.visible === false ) return;\n\n var visible = object.layers.test( camera.layers );\n\n if ( visible ) {\n\n if ( object.isLight ) {\n\n currentRenderState.pushLight( object );\n\n if ( object.castShadow ) {\n\n currentRenderState.pushShadow( object );\n\n }\n\n } else if ( object.isSprite ) {\n\n if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {\n\n currentRenderState.pushSprite( object );\n\n }\n\n } else if ( object.isImmediateRenderObject ) {\n\n if ( sortObjects ) {\n\n _vector3.setFromMatrixPosition( object.matrixWorld )\n .applyMatrix4( _projScreenMatrix );\n\n }\n\n currentRenderList.push( object, null, object.material, _vector3.z, null );\n\n } else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n if ( object.isSkinnedMesh ) {\n\n object.skeleton.update();\n\n }\n\n if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {\n\n if ( sortObjects ) {\n\n _vector3.setFromMatrixPosition( object.matrixWorld )\n .applyMatrix4( _projScreenMatrix );\n\n }\n\n var geometry = objects.update( object );\n var material = object.material;\n\n if ( Array.isArray( material ) ) {\n\n var groups = geometry.groups;\n\n for ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n var groupMaterial = material[ group.materialIndex ];\n\n if ( groupMaterial && groupMaterial.visible ) {\n\n currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group );\n\n }\n\n }\n\n } else if ( material.visible ) {\n\n currentRenderList.push( object, geometry, material, _vector3.z, null );\n\n }\n\n }\n\n }\n\n }\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n projectObject( children[ i ], camera, sortObjects );\n\n }\n\n }\n\n function renderObjects( renderList, scene, camera, overrideMaterial ) {\n\n for ( var i = 0, l = renderList.length; i < l; i ++ ) {\n\n var renderItem = renderList[ i ];\n\n var object = renderItem.object;\n var geometry = renderItem.geometry;\n var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;\n var group = renderItem.group;\n\n if ( camera.isArrayCamera ) {\n\n _currentArrayCamera = camera;\n\n var cameras = camera.cameras;\n\n for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {\n\n var camera2 = cameras[ j ];\n\n if ( object.layers.test( camera2.layers ) ) {\n\n var bounds = camera2.bounds;\n\n var x = bounds.x * _width;\n var y = bounds.y * _height;\n var width = bounds.z * _width;\n var height = bounds.w * _height;\n\n state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );\n\n renderObject( object, scene, camera2, geometry, material, group );\n\n }\n\n }\n\n } else {\n\n _currentArrayCamera = null;\n\n renderObject( object, scene, camera, geometry, material, group );\n\n }\n\n }\n\n }\n\n function renderObject( object, scene, camera, geometry, material, group ) {\n\n object.onBeforeRender( _this, scene, camera, geometry, material, group );\n currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );\n\n object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n object.normalMatrix.getNormalMatrix( object.modelViewMatrix );\n\n if ( object.isImmediateRenderObject ) {\n\n var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n state.setMaterial( material, frontFaceCW );\n\n var program = setProgram( camera, scene.fog, material, object );\n\n _currentGeometryProgram = '';\n\n renderObjectImmediate( object, program, material );\n\n } else {\n\n _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group );\n\n }\n\n object.onAfterRender( _this, scene, camera, geometry, material, group );\n currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );\n\n }\n\n function initMaterial( material, fog, object ) {\n\n var materialProperties = properties.get( material );\n\n var lights = currentRenderState.state.lights;\n var shadowsArray = currentRenderState.state.shadowsArray;\n\n var parameters = programCache.getParameters(\n material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object );\n\n var code = programCache.getProgramCode( material, parameters );\n\n var program = materialProperties.program;\n var programChange = true;\n\n if ( program === undefined ) {\n\n // new material\n material.addEventListener( 'dispose', onMaterialDispose );\n\n } else if ( program.code !== code ) {\n\n // changed glsl or parameters\n releaseMaterialProgramReference( material );\n\n } else if ( materialProperties.lightsHash !== lights.state.hash ) {\n\n properties.update( material, 'lightsHash', lights.state.hash );\n programChange = false;\n\n } else if ( parameters.shaderID !== undefined ) {\n\n // same glsl and uniform list\n return;\n\n } else {\n\n // only rebuild uniform list\n programChange = false;\n\n }\n\n if ( programChange ) {\n\n if ( parameters.shaderID ) {\n\n var shader = ShaderLib[ parameters.shaderID ];\n\n materialProperties.shader = {\n name: material.type,\n uniforms: UniformsUtils.clone( shader.uniforms ),\n vertexShader: shader.vertexShader,\n fragmentShader: shader.fragmentShader\n };\n\n } else {\n\n materialProperties.shader = {\n name: material.type,\n uniforms: material.uniforms,\n vertexShader: material.vertexShader,\n fragmentShader: material.fragmentShader\n };\n\n }\n\n material.onBeforeCompile( materialProperties.shader, _this );\n\n program = programCache.acquireProgram( material, materialProperties.shader, parameters, code );\n\n materialProperties.program = program;\n material.program = program;\n\n }\n\n var programAttributes = program.getAttributes();\n\n if ( material.morphTargets ) {\n\n material.numSupportedMorphTargets = 0;\n\n for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {\n\n if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {\n\n material.numSupportedMorphTargets ++;\n\n }\n\n }\n\n }\n\n if ( material.morphNormals ) {\n\n material.numSupportedMorphNormals = 0;\n\n for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {\n\n if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {\n\n material.numSupportedMorphNormals ++;\n\n }\n\n }\n\n }\n\n var uniforms = materialProperties.shader.uniforms;\n\n if ( ! material.isShaderMaterial &&\n ! material.isRawShaderMaterial ||\n material.clipping === true ) {\n\n materialProperties.numClippingPlanes = _clipping.numPlanes;\n materialProperties.numIntersection = _clipping.numIntersection;\n uniforms.clippingPlanes = _clipping.uniform;\n\n }\n\n materialProperties.fog = fog;\n\n // store the light setup it was created for\n\n materialProperties.lightsHash = lights.state.hash;\n\n if ( material.lights ) {\n\n // wire up the material to this renderer's lighting state\n\n uniforms.ambientLightColor.value = lights.state.ambient;\n uniforms.directionalLights.value = lights.state.directional;\n uniforms.spotLights.value = lights.state.spot;\n uniforms.rectAreaLights.value = lights.state.rectArea;\n uniforms.pointLights.value = lights.state.point;\n uniforms.hemisphereLights.value = lights.state.hemi;\n\n uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;\n uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;\n uniforms.spotShadowMap.value = lights.state.spotShadowMap;\n uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;\n uniforms.pointShadowMap.value = lights.state.pointShadowMap;\n uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;\n // TODO (abelnation): add area lights shadow info to uniforms\n\n }\n\n var progUniforms = materialProperties.program.getUniforms(),\n uniformsList =\n WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );\n\n materialProperties.uniformsList = uniformsList;\n\n }\n\n function setProgram( camera, fog, material, object ) {\n\n _usedTextureUnits = 0;\n\n var materialProperties = properties.get( material );\n var lights = currentRenderState.state.lights;\n\n if ( _clippingEnabled ) {\n\n if ( _localClippingEnabled || camera !== _currentCamera ) {\n\n var useCache =\n camera === _currentCamera &&\n material.id === _currentMaterialId;\n\n // we might want to call this function with some ClippingGroup\n // object instead of the material, once it becomes feasible\n // (#8465, #8379)\n _clipping.setState(\n material.clippingPlanes, material.clipIntersection, material.clipShadows,\n camera, materialProperties, useCache );\n\n }\n\n }\n\n if ( material.needsUpdate === false ) {\n\n if ( materialProperties.program === undefined ) {\n\n material.needsUpdate = true;\n\n } else if ( material.fog && materialProperties.fog !== fog ) {\n\n material.needsUpdate = true;\n\n } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) {\n\n material.needsUpdate = true;\n\n } else if ( materialProperties.numClippingPlanes !== undefined &&\n ( materialProperties.numClippingPlanes !== _clipping.numPlanes ||\n materialProperties.numIntersection !== _clipping.numIntersection ) ) {\n\n material.needsUpdate = true;\n\n }\n\n }\n\n if ( material.needsUpdate ) {\n\n initMaterial( material, fog, object );\n material.needsUpdate = false;\n\n }\n\n var refreshProgram = false;\n var refreshMaterial = false;\n var refreshLights = false;\n\n var program = materialProperties.program,\n p_uniforms = program.getUniforms(),\n m_uniforms = materialProperties.shader.uniforms;\n\n if ( state.useProgram( program.program ) ) {\n\n refreshProgram = true;\n refreshMaterial = true;\n refreshLights = true;\n\n }\n\n if ( material.id !== _currentMaterialId ) {\n\n _currentMaterialId = material.id;\n\n refreshMaterial = true;\n\n }\n\n if ( refreshProgram || camera !== _currentCamera ) {\n\n p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );\n\n if ( capabilities.logarithmicDepthBuffer ) {\n\n p_uniforms.setValue( _gl, 'logDepthBufFC',\n 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );\n\n }\n\n // Avoid unneeded uniform updates per ArrayCamera's sub-camera\n\n if ( _currentCamera !== ( _currentArrayCamera || camera ) ) {\n\n _currentCamera = ( _currentArrayCamera || camera );\n\n // lighting uniforms depend on the camera so enforce an update\n // now, in case this material supports lights - or later, when\n // the next material that does gets activated:\n\n refreshMaterial = true; // set to true on material change\n refreshLights = true; // remains set until update done\n\n }\n\n // load material specific uniforms\n // (shader material also gets them for the sake of genericity)\n\n if ( material.isShaderMaterial ||\n material.isMeshPhongMaterial ||\n material.isMeshStandardMaterial ||\n material.envMap ) {\n\n var uCamPos = p_uniforms.map.cameraPosition;\n\n if ( uCamPos !== undefined ) {\n\n uCamPos.setValue( _gl,\n _vector3.setFromMatrixPosition( camera.matrixWorld ) );\n\n }\n\n }\n\n if ( material.isMeshPhongMaterial ||\n material.isMeshLambertMaterial ||\n material.isMeshBasicMaterial ||\n material.isMeshStandardMaterial ||\n material.isShaderMaterial ||\n material.skinning ) {\n\n p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );\n\n }\n\n }\n\n // skinning uniforms must be set even if material didn't change\n // auto-setting of texture unit for bone texture must go before other textures\n // not sure why, but otherwise weird things happen\n\n if ( material.skinning ) {\n\n p_uniforms.setOptional( _gl, object, 'bindMatrix' );\n p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );\n\n var skeleton = object.skeleton;\n\n if ( skeleton ) {\n\n var bones = skeleton.bones;\n\n if ( capabilities.floatVertexTextures ) {\n\n if ( skeleton.boneTexture === undefined ) {\n\n // layout (1 matrix = 4 pixels)\n // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)\n // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)\n // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)\n // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)\n // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)\n\n\n var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix\n size = _Math.ceilPowerOfTwo( size );\n size = Math.max( size, 4 );\n\n var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel\n boneMatrices.set( skeleton.boneMatrices ); // copy current values\n\n var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );\n boneTexture.needsUpdate = true;\n\n skeleton.boneMatrices = boneMatrices;\n skeleton.boneTexture = boneTexture;\n skeleton.boneTextureSize = size;\n\n }\n\n p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture );\n p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );\n\n } else {\n\n p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );\n\n }\n\n }\n\n }\n\n if ( refreshMaterial ) {\n\n p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );\n p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );\n\n if ( material.lights ) {\n\n // the current material requires lighting info\n\n // note: all lighting uniforms are always set correctly\n // they simply reference the renderer's state for their\n // values\n //\n // use the current material's .needsUpdate flags to set\n // the GL state when required\n\n markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );\n\n }\n\n // refresh uniforms common to several materials\n\n if ( fog && material.fog ) {\n\n refreshUniformsFog( m_uniforms, fog );\n\n }\n\n if ( material.isMeshBasicMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n } else if ( material.isMeshLambertMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsLambert( m_uniforms, material );\n\n } else if ( material.isMeshPhongMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n if ( material.isMeshToonMaterial ) {\n\n refreshUniformsToon( m_uniforms, material );\n\n } else {\n\n refreshUniformsPhong( m_uniforms, material );\n\n }\n\n } else if ( material.isMeshStandardMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n if ( material.isMeshPhysicalMaterial ) {\n\n refreshUniformsPhysical( m_uniforms, material );\n\n } else {\n\n refreshUniformsStandard( m_uniforms, material );\n\n }\n\n } else if ( material.isMeshDepthMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsDepth( m_uniforms, material );\n\n } else if ( material.isMeshDistanceMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsDistance( m_uniforms, material );\n\n } else if ( material.isMeshNormalMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsNormal( m_uniforms, material );\n\n } else if ( material.isLineBasicMaterial ) {\n\n refreshUniformsLine( m_uniforms, material );\n\n if ( material.isLineDashedMaterial ) {\n\n refreshUniformsDash( m_uniforms, material );\n\n }\n\n } else if ( material.isPointsMaterial ) {\n\n refreshUniformsPoints( m_uniforms, material );\n\n } else if ( material.isShadowMaterial ) {\n\n m_uniforms.color.value = material.color;\n m_uniforms.opacity.value = material.opacity;\n\n }\n\n // RectAreaLight Texture\n // TODO (mrdoob): Find a nicer implementation\n\n if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1;\n if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2;\n\n WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );\n\n }\n\n if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {\n\n WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );\n material.uniformsNeedUpdate = false;\n\n }\n\n // common matrices\n\n p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );\n p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );\n p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );\n\n return program;\n\n }\n\n // Uniforms (refresh uniforms objects)\n\n function refreshUniformsCommon( uniforms, material ) {\n\n uniforms.opacity.value = material.opacity;\n\n if ( material.color ) {\n\n uniforms.diffuse.value = material.color;\n\n }\n\n if ( material.emissive ) {\n\n uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );\n\n }\n\n if ( material.map ) {\n\n uniforms.map.value = material.map;\n\n }\n\n if ( material.alphaMap ) {\n\n uniforms.alphaMap.value = material.alphaMap;\n\n }\n\n if ( material.specularMap ) {\n\n uniforms.specularMap.value = material.specularMap;\n\n }\n\n if ( material.envMap ) {\n\n uniforms.envMap.value = material.envMap;\n\n // don't flip CubeTexture envMaps, flip everything else:\n // WebGLRenderTargetCube will be flipped for backwards compatibility\n // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture\n // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future\n uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;\n\n uniforms.reflectivity.value = material.reflectivity;\n uniforms.refractionRatio.value = material.refractionRatio;\n\n uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel;\n\n }\n\n if ( material.lightMap ) {\n\n uniforms.lightMap.value = material.lightMap;\n uniforms.lightMapIntensity.value = material.lightMapIntensity;\n\n }\n\n if ( material.aoMap ) {\n\n uniforms.aoMap.value = material.aoMap;\n uniforms.aoMapIntensity.value = material.aoMapIntensity;\n\n }\n\n // uv repeat and offset setting priorities\n // 1. color map\n // 2. specular map\n // 3. normal map\n // 4. bump map\n // 5. alpha map\n // 6. emissive map\n\n var uvScaleMap;\n\n if ( material.map ) {\n\n uvScaleMap = material.map;\n\n } else if ( material.specularMap ) {\n\n uvScaleMap = material.specularMap;\n\n } else if ( material.displacementMap ) {\n\n uvScaleMap = material.displacementMap;\n\n } else if ( material.normalMap ) {\n\n uvScaleMap = material.normalMap;\n\n } else if ( material.bumpMap ) {\n\n uvScaleMap = material.bumpMap;\n\n } else if ( material.roughnessMap ) {\n\n uvScaleMap = material.roughnessMap;\n\n } else if ( material.metalnessMap ) {\n\n uvScaleMap = material.metalnessMap;\n\n } else if ( material.alphaMap ) {\n\n uvScaleMap = material.alphaMap;\n\n } else if ( material.emissiveMap ) {\n\n uvScaleMap = material.emissiveMap;\n\n }\n\n if ( uvScaleMap !== undefined ) {\n\n // backwards compatibility\n if ( uvScaleMap.isWebGLRenderTarget ) {\n\n uvScaleMap = uvScaleMap.texture;\n\n }\n\n if ( uvScaleMap.matrixAutoUpdate === true ) {\n\n var offset = uvScaleMap.offset;\n var repeat = uvScaleMap.repeat;\n var rotation = uvScaleMap.rotation;\n var center = uvScaleMap.center;\n\n uvScaleMap.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y );\n\n }\n\n uniforms.uvTransform.value.copy( uvScaleMap.matrix );\n\n }\n\n }\n\n function refreshUniformsLine( uniforms, material ) {\n\n uniforms.diffuse.value = material.color;\n uniforms.opacity.value = material.opacity;\n\n }\n\n function refreshUniformsDash( uniforms, material ) {\n\n uniforms.dashSize.value = material.dashSize;\n uniforms.totalSize.value = material.dashSize + material.gapSize;\n uniforms.scale.value = material.scale;\n\n }\n\n function refreshUniformsPoints( uniforms, material ) {\n\n uniforms.diffuse.value = material.color;\n uniforms.opacity.value = material.opacity;\n uniforms.size.value = material.size * _pixelRatio;\n uniforms.scale.value = _height * 0.5;\n\n uniforms.map.value = material.map;\n\n if ( material.map !== null ) {\n\n if ( material.map.matrixAutoUpdate === true ) {\n\n var offset = material.map.offset;\n var repeat = material.map.repeat;\n var rotation = material.map.rotation;\n var center = material.map.center;\n\n material.map.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y );\n\n }\n\n uniforms.uvTransform.value.copy( material.map.matrix );\n\n }\n\n }\n\n function refreshUniformsFog( uniforms, fog ) {\n\n uniforms.fogColor.value = fog.color;\n\n if ( fog.isFog ) {\n\n uniforms.fogNear.value = fog.near;\n uniforms.fogFar.value = fog.far;\n\n } else if ( fog.isFogExp2 ) {\n\n uniforms.fogDensity.value = fog.density;\n\n }\n\n }\n\n function refreshUniformsLambert( uniforms, material ) {\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n }\n\n function refreshUniformsPhong( uniforms, material ) {\n\n uniforms.specular.value = material.specular;\n uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n function refreshUniformsToon( uniforms, material ) {\n\n refreshUniformsPhong( uniforms, material );\n\n if ( material.gradientMap ) {\n\n uniforms.gradientMap.value = material.gradientMap;\n\n }\n\n }\n\n function refreshUniformsStandard( uniforms, material ) {\n\n uniforms.roughness.value = material.roughness;\n uniforms.metalness.value = material.metalness;\n\n if ( material.roughnessMap ) {\n\n uniforms.roughnessMap.value = material.roughnessMap;\n\n }\n\n if ( material.metalnessMap ) {\n\n uniforms.metalnessMap.value = material.metalnessMap;\n\n }\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n if ( material.envMap ) {\n\n //uniforms.envMap.value = material.envMap; // part of uniforms common\n uniforms.envMapIntensity.value = material.envMapIntensity;\n\n }\n\n }\n\n function refreshUniformsPhysical( uniforms, material ) {\n\n uniforms.clearCoat.value = material.clearCoat;\n uniforms.clearCoatRoughness.value = material.clearCoatRoughness;\n\n refreshUniformsStandard( uniforms, material );\n\n }\n\n function refreshUniformsDepth( uniforms, material ) {\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n function refreshUniformsDistance( uniforms, material ) {\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n uniforms.referencePosition.value.copy( material.referencePosition );\n uniforms.nearDistance.value = material.nearDistance;\n uniforms.farDistance.value = material.farDistance;\n\n }\n\n function refreshUniformsNormal( uniforms, material ) {\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n // If uniforms are marked as clean, they don't need to be loaded to the GPU.\n\n function markUniformsLightsNeedsUpdate( uniforms, value ) {\n\n uniforms.ambientLightColor.needsUpdate = value;\n\n uniforms.directionalLights.needsUpdate = value;\n uniforms.pointLights.needsUpdate = value;\n uniforms.spotLights.needsUpdate = value;\n uniforms.rectAreaLights.needsUpdate = value;\n uniforms.hemisphereLights.needsUpdate = value;\n\n }\n\n // Textures\n\n function allocTextureUnit() {\n\n var textureUnit = _usedTextureUnits;\n\n if ( textureUnit >= capabilities.maxTextures ) {\n\n console.warn( 'THREE.WebGLRenderer: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );\n\n }\n\n _usedTextureUnits += 1;\n\n return textureUnit;\n\n }\n\n this.allocTextureUnit = allocTextureUnit;\n\n // this.setTexture2D = setTexture2D;\n this.setTexture2D = ( function () {\n\n var warned = false;\n\n // backwards compatibility: peel texture.texture\n return function setTexture2D( texture, slot ) {\n\n if ( texture && texture.isWebGLRenderTarget ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead.\" );\n warned = true;\n\n }\n\n texture = texture.texture;\n\n }\n\n textures.setTexture2D( texture, slot );\n\n };\n\n }() );\n\n this.setTexture = ( function () {\n\n var warned = false;\n\n return function setTexture( texture, slot ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead.\" );\n warned = true;\n\n }\n\n textures.setTexture2D( texture, slot );\n\n };\n\n }() );\n\n this.setTextureCube = ( function () {\n\n var warned = false;\n\n return function setTextureCube( texture, slot ) {\n\n // backwards compatibility: peel texture.texture\n if ( texture && texture.isWebGLRenderTargetCube ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead.\" );\n warned = true;\n\n }\n\n texture = texture.texture;\n\n }\n\n // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture\n // TODO: unify these code paths\n if ( ( texture && texture.isCubeTexture ) ||\n ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {\n\n // CompressedTexture can have Array in image :/\n\n // this function alone should take care of cube textures\n textures.setTextureCube( texture, slot );\n\n } else {\n\n // assumed: texture property of THREE.WebGLRenderTargetCube\n\n textures.setTextureCubeDynamic( texture, slot );\n\n }\n\n };\n\n }() );\n\n this.getRenderTarget = function () {\n\n return _currentRenderTarget;\n\n };\n\n this.setRenderTarget = function ( renderTarget ) {\n\n _currentRenderTarget = renderTarget;\n\n if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {\n\n textures.setupRenderTarget( renderTarget );\n\n }\n\n var framebuffer = null;\n var isCube = false;\n\n if ( renderTarget ) {\n\n var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n if ( renderTarget.isWebGLRenderTargetCube ) {\n\n framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ];\n isCube = true;\n\n } else {\n\n framebuffer = __webglFramebuffer;\n\n }\n\n _currentViewport.copy( renderTarget.viewport );\n _currentScissor.copy( renderTarget.scissor );\n _currentScissorTest = renderTarget.scissorTest;\n\n } else {\n\n _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio );\n _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio );\n _currentScissorTest = _scissorTest;\n\n }\n\n if ( _currentFramebuffer !== framebuffer ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n _currentFramebuffer = framebuffer;\n\n }\n\n state.viewport( _currentViewport );\n state.scissor( _currentScissor );\n state.setScissorTest( _currentScissorTest );\n\n if ( isCube ) {\n\n var textureProperties = properties.get( renderTarget.texture );\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel );\n\n }\n\n };\n\n this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {\n\n if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );\n return;\n\n }\n\n var framebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n if ( framebuffer ) {\n\n var restore = false;\n\n if ( framebuffer !== _currentFramebuffer ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n restore = true;\n\n }\n\n try {\n\n var texture = renderTarget.texture;\n var textureFormat = texture.format;\n var textureType = texture.type;\n\n if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );\n return;\n\n }\n\n if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513)\n ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox\n ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );\n return;\n\n }\n\n if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {\n\n // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)\n\n if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {\n\n _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );\n\n }\n\n } else {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );\n\n }\n\n } finally {\n\n if ( restore ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );\n\n }\n\n }\n\n }\n\n };\n\n this.copyFramebufferToTexture = function ( position, texture, level ) {\n\n var width = texture.image.width;\n var height = texture.image.height;\n var glFormat = utils.convert( texture.format );\n\n this.setTexture2D( texture, 0 );\n\n _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 );\n\n };\n\n this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) {\n\n var width = srcTexture.image.width;\n var height = srcTexture.image.height;\n var glFormat = utils.convert( dstTexture.format );\n var glType = utils.convert( dstTexture.type );\n var pixels = srcTexture.isDataTexture ? srcTexture.image.data : srcTexture.image;\n\n this.setTexture2D( dstTexture, 0 );\n\n _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, pixels );\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function FogExp2( color, density ) {\n\n this.name = '';\n\n this.color = new Color( color );\n this.density = ( density !== undefined ) ? density : 0.00025;\n\n }\n\n FogExp2.prototype.isFogExp2 = true;\n\n FogExp2.prototype.clone = function () {\n\n return new FogExp2( this.color.getHex(), this.density );\n\n };\n\n FogExp2.prototype.toJSON = function ( /* meta */ ) {\n\n return {\n type: 'FogExp2',\n color: this.color.getHex(),\n density: this.density\n };\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Fog( color, near, far ) {\n\n this.name = '';\n\n this.color = new Color( color );\n\n this.near = ( near !== undefined ) ? near : 1;\n this.far = ( far !== undefined ) ? far : 1000;\n\n }\n\n Fog.prototype.isFog = true;\n\n Fog.prototype.clone = function () {\n\n return new Fog( this.color.getHex(), this.near, this.far );\n\n };\n\n Fog.prototype.toJSON = function ( /* meta */ ) {\n\n return {\n type: 'Fog',\n color: this.color.getHex(),\n near: this.near,\n far: this.far\n };\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Scene() {\n\n Object3D.call( this );\n\n this.type = 'Scene';\n\n this.background = null;\n this.fog = null;\n this.overrideMaterial = null;\n\n this.autoUpdate = true; // checked by the renderer\n\n }\n\n Scene.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Scene,\n\n copy: function ( source, recursive ) {\n\n Object3D.prototype.copy.call( this, source, recursive );\n\n if ( source.background !== null ) this.background = source.background.clone();\n if ( source.fog !== null ) this.fog = source.fog.clone();\n if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();\n\n this.autoUpdate = source.autoUpdate;\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n if ( this.background !== null ) data.object.background = this.background.toJSON( meta );\n if ( this.fog !== null ) data.object.fog = this.fog.toJSON();\n\n return data;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * uvOffset: new THREE.Vector2(),\n * uvScale: new THREE.Vector2()\n * }\n */\n\n function SpriteMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'SpriteMaterial';\n\n this.color = new Color( 0xffffff );\n this.map = null;\n\n this.rotation = 0;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n SpriteMaterial.prototype = Object.create( Material.prototype );\n SpriteMaterial.prototype.constructor = SpriteMaterial;\n SpriteMaterial.prototype.isSpriteMaterial = true;\n\n SpriteMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.map = source.map;\n\n this.rotation = source.rotation;\n\n return this;\n\n };\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Sprite( material ) {\n\n Object3D.call( this );\n\n this.type = 'Sprite';\n\n this.material = ( material !== undefined ) ? material : new SpriteMaterial();\n\n this.center = new Vector2( 0.5, 0.5 );\n\n }\n\n Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Sprite,\n\n isSprite: true,\n\n raycast: ( function () {\n\n var intersectPoint = new Vector3();\n var worldPosition = new Vector3();\n var worldScale = new Vector3();\n\n return function raycast( raycaster, intersects ) {\n\n worldPosition.setFromMatrixPosition( this.matrixWorld );\n raycaster.ray.closestPointToPoint( worldPosition, intersectPoint );\n\n worldScale.setFromMatrixScale( this.matrixWorld );\n var guessSizeSq = worldScale.x * worldScale.y / 4;\n\n if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return;\n\n var distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return;\n\n intersects.push( {\n\n distance: distance,\n point: intersectPoint.clone(),\n face: null,\n object: this\n\n } );\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.material ).copy( this );\n\n },\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n if ( source.center !== undefined ) this.center.copy( source.center );\n\n return this;\n\n }\n\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LOD() {\n\n Object3D.call( this );\n\n this.type = 'LOD';\n\n Object.defineProperties( this, {\n levels: {\n enumerable: true,\n value: []\n }\n } );\n\n }\n\n LOD.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: LOD,\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source, false );\n\n var levels = source.levels;\n\n for ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n var level = levels[ i ];\n\n this.addLevel( level.object.clone(), level.distance );\n\n }\n\n return this;\n\n },\n\n addLevel: function ( object, distance ) {\n\n if ( distance === undefined ) distance = 0;\n\n distance = Math.abs( distance );\n\n var levels = this.levels;\n\n for ( var l = 0; l < levels.length; l ++ ) {\n\n if ( distance < levels[ l ].distance ) {\n\n break;\n\n }\n\n }\n\n levels.splice( l, 0, { distance: distance, object: object } );\n\n this.add( object );\n\n },\n\n getObjectForDistance: function ( distance ) {\n\n var levels = this.levels;\n\n for ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n if ( distance < levels[ i ].distance ) {\n\n break;\n\n }\n\n }\n\n return levels[ i - 1 ].object;\n\n },\n\n raycast: ( function () {\n\n var matrixPosition = new Vector3();\n\n return function raycast( raycaster, intersects ) {\n\n matrixPosition.setFromMatrixPosition( this.matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( matrixPosition );\n\n this.getObjectForDistance( distance ).raycast( raycaster, intersects );\n\n };\n\n }() ),\n\n update: function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function update( camera ) {\n\n var levels = this.levels;\n\n if ( levels.length > 1 ) {\n\n v1.setFromMatrixPosition( camera.matrixWorld );\n v2.setFromMatrixPosition( this.matrixWorld );\n\n var distance = v1.distanceTo( v2 );\n\n levels[ 0 ].object.visible = true;\n\n for ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n if ( distance >= levels[ i ].distance ) {\n\n levels[ i - 1 ].object.visible = false;\n levels[ i ].object.visible = true;\n\n } else {\n\n break;\n\n }\n\n }\n\n for ( ; i < l; i ++ ) {\n\n levels[ i ].object.visible = false;\n\n }\n\n }\n\n };\n\n }(),\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.levels = [];\n\n var levels = this.levels;\n\n for ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n var level = levels[ i ];\n\n data.object.levels.push( {\n object: level.object.uuid,\n distance: level.distance\n } );\n\n }\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author michael guerrero / http://realitymeltdown.com\n * @author ikerr / http://verold.com\n */\n\n function Skeleton( bones, boneInverses ) {\n\n // copy the bone array\n\n bones = bones || [];\n\n this.bones = bones.slice( 0 );\n this.boneMatrices = new Float32Array( this.bones.length * 16 );\n\n // use the supplied bone inverses or calculate the inverses\n\n if ( boneInverses === undefined ) {\n\n this.calculateInverses();\n\n } else {\n\n if ( this.bones.length === boneInverses.length ) {\n\n this.boneInverses = boneInverses.slice( 0 );\n\n } else {\n\n console.warn( 'THREE.Skeleton boneInverses is the wrong length.' );\n\n this.boneInverses = [];\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n this.boneInverses.push( new Matrix4() );\n\n }\n\n }\n\n }\n\n }\n\n Object.assign( Skeleton.prototype, {\n\n calculateInverses: function () {\n\n this.boneInverses = [];\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n var inverse = new Matrix4();\n\n if ( this.bones[ i ] ) {\n\n inverse.getInverse( this.bones[ i ].matrixWorld );\n\n }\n\n this.boneInverses.push( inverse );\n\n }\n\n },\n\n pose: function () {\n\n var bone, i, il;\n\n // recover the bind-time world matrices\n\n for ( i = 0, il = this.bones.length; i < il; i ++ ) {\n\n bone = this.bones[ i ];\n\n if ( bone ) {\n\n bone.matrixWorld.getInverse( this.boneInverses[ i ] );\n\n }\n\n }\n\n // compute the local matrices, positions, rotations and scales\n\n for ( i = 0, il = this.bones.length; i < il; i ++ ) {\n\n bone = this.bones[ i ];\n\n if ( bone ) {\n\n if ( bone.parent && bone.parent.isBone ) {\n\n bone.matrix.getInverse( bone.parent.matrixWorld );\n bone.matrix.multiply( bone.matrixWorld );\n\n } else {\n\n bone.matrix.copy( bone.matrixWorld );\n\n }\n\n bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );\n\n }\n\n }\n\n },\n\n update: ( function () {\n\n var offsetMatrix = new Matrix4();\n var identityMatrix = new Matrix4();\n\n return function update() {\n\n var bones = this.bones;\n var boneInverses = this.boneInverses;\n var boneMatrices = this.boneMatrices;\n var boneTexture = this.boneTexture;\n\n // flatten bone matrices to array\n\n for ( var i = 0, il = bones.length; i < il; i ++ ) {\n\n // compute the offset between the current and the original transform\n\n var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix;\n\n offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );\n offsetMatrix.toArray( boneMatrices, i * 16 );\n\n }\n\n if ( boneTexture !== undefined ) {\n\n boneTexture.needsUpdate = true;\n\n }\n\n };\n\n } )(),\n\n clone: function () {\n\n return new Skeleton( this.bones, this.boneInverses );\n\n },\n\n getBoneByName: function ( name ) {\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n var bone = this.bones[ i ];\n\n if ( bone.name === name ) {\n\n return bone;\n\n }\n\n }\n\n return undefined;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author ikerr / http://verold.com\n */\n\n function Bone() {\n\n Object3D.call( this );\n\n this.type = 'Bone';\n\n }\n\n Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Bone,\n\n isBone: true\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author ikerr / http://verold.com\n */\n\n function SkinnedMesh( geometry, material ) {\n\n Mesh.call( this, geometry, material );\n\n this.type = 'SkinnedMesh';\n\n this.bindMode = 'attached';\n this.bindMatrix = new Matrix4();\n this.bindMatrixInverse = new Matrix4();\n\n var bones = this.initBones();\n var skeleton = new Skeleton( bones );\n\n this.bind( skeleton, this.matrixWorld );\n\n this.normalizeSkinWeights();\n\n }\n\n SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {\n\n constructor: SkinnedMesh,\n\n isSkinnedMesh: true,\n\n initBones: function () {\n\n var bones = [], bone, gbone;\n var i, il;\n\n if ( this.geometry && this.geometry.bones !== undefined ) {\n\n // first, create array of 'Bone' objects from geometry data\n\n for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {\n\n gbone = this.geometry.bones[ i ];\n\n // create new 'Bone' object\n\n bone = new Bone();\n bones.push( bone );\n\n // apply values\n\n bone.name = gbone.name;\n bone.position.fromArray( gbone.pos );\n bone.quaternion.fromArray( gbone.rotq );\n if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );\n\n }\n\n // second, create bone hierarchy\n\n for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {\n\n gbone = this.geometry.bones[ i ];\n\n if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {\n\n // subsequent bones in the hierarchy\n\n bones[ gbone.parent ].add( bones[ i ] );\n\n } else {\n\n // topmost bone, immediate child of the skinned mesh\n\n this.add( bones[ i ] );\n\n }\n\n }\n\n }\n\n // now the bones are part of the scene graph and children of the skinned mesh.\n // let's update the corresponding matrices\n\n this.updateMatrixWorld( true );\n\n return bones;\n\n },\n\n bind: function ( skeleton, bindMatrix ) {\n\n this.skeleton = skeleton;\n\n if ( bindMatrix === undefined ) {\n\n this.updateMatrixWorld( true );\n\n this.skeleton.calculateInverses();\n\n bindMatrix = this.matrixWorld;\n\n }\n\n this.bindMatrix.copy( bindMatrix );\n this.bindMatrixInverse.getInverse( bindMatrix );\n\n },\n\n pose: function () {\n\n this.skeleton.pose();\n\n },\n\n normalizeSkinWeights: function () {\n\n var scale, i;\n\n if ( this.geometry && this.geometry.isGeometry ) {\n\n for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) {\n\n var sw = this.geometry.skinWeights[ i ];\n\n scale = 1.0 / sw.manhattanLength();\n\n if ( scale !== Infinity ) {\n\n sw.multiplyScalar( scale );\n\n } else {\n\n sw.set( 1, 0, 0, 0 ); // do something reasonable\n\n }\n\n }\n\n } else if ( this.geometry && this.geometry.isBufferGeometry ) {\n\n var vec = new Vector4();\n\n var skinWeight = this.geometry.attributes.skinWeight;\n\n for ( i = 0; i < skinWeight.count; i ++ ) {\n\n vec.x = skinWeight.getX( i );\n vec.y = skinWeight.getY( i );\n vec.z = skinWeight.getZ( i );\n vec.w = skinWeight.getW( i );\n\n scale = 1.0 / vec.manhattanLength();\n\n if ( scale !== Infinity ) {\n\n vec.multiplyScalar( scale );\n\n } else {\n\n vec.set( 1, 0, 0, 0 ); // do something reasonable\n\n }\n\n skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w );\n\n }\n\n }\n\n },\n\n updateMatrixWorld: function ( force ) {\n\n Mesh.prototype.updateMatrixWorld.call( this, force );\n\n if ( this.bindMode === 'attached' ) {\n\n this.bindMatrixInverse.getInverse( this.matrixWorld );\n\n } else if ( this.bindMode === 'detached' ) {\n\n this.bindMatrixInverse.getInverse( this.bindMatrix );\n\n } else {\n\n console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * linewidth: <float>,\n * linecap: \"round\",\n * linejoin: \"round\"\n * }\n */\n\n function LineBasicMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'LineBasicMaterial';\n\n this.color = new Color( 0xffffff );\n\n this.linewidth = 1;\n this.linecap = 'round';\n this.linejoin = 'round';\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n LineBasicMaterial.prototype = Object.create( Material.prototype );\n LineBasicMaterial.prototype.constructor = LineBasicMaterial;\n\n LineBasicMaterial.prototype.isLineBasicMaterial = true;\n\n LineBasicMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.linewidth = source.linewidth;\n this.linecap = source.linecap;\n this.linejoin = source.linejoin;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Line( geometry, material, mode ) {\n\n if ( mode === 1 ) {\n\n console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );\n return new LineSegments( geometry, material );\n\n }\n\n Object3D.call( this );\n\n this.type = 'Line';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } );\n\n }\n\n Line.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Line,\n\n isLine: true,\n\n computeLineDistances: ( function () {\n\n var start = new Vector3();\n var end = new Vector3();\n\n return function computeLineDistances() {\n\n var geometry = this.geometry;\n\n if ( geometry.isBufferGeometry ) {\n\n // we assume non-indexed geometry\n\n if ( geometry.index === null ) {\n\n var positionAttribute = geometry.attributes.position;\n var lineDistances = [ 0 ];\n\n for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) {\n\n start.fromBufferAttribute( positionAttribute, i - 1 );\n end.fromBufferAttribute( positionAttribute, i );\n\n lineDistances[ i ] = lineDistances[ i - 1 ];\n lineDistances[ i ] += start.distanceTo( end );\n\n }\n\n geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) );\n\n } else {\n\n console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var lineDistances = geometry.lineDistances;\n\n lineDistances[ 0 ] = 0;\n\n for ( var i = 1, l = vertices.length; i < l; i ++ ) {\n\n lineDistances[ i ] = lineDistances[ i - 1 ];\n lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] );\n\n }\n\n }\n\n return this;\n\n };\n\n }() ),\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n return function raycast( raycaster, intersects ) {\n\n var precision = raycaster.linePrecision;\n var precisionSq = precision * precision;\n\n var geometry = this.geometry;\n var matrixWorld = this.matrixWorld;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n var vStart = new Vector3();\n var vEnd = new Vector3();\n var interSegment = new Vector3();\n var interRay = new Vector3();\n var step = ( this && this.isLineSegments ) ? 2 : 1;\n\n if ( geometry.isBufferGeometry ) {\n\n var index = geometry.index;\n var attributes = geometry.attributes;\n var positions = attributes.position.array;\n\n if ( index !== null ) {\n\n var indices = index.array;\n\n for ( var i = 0, l = indices.length - 1; i < l; i += step ) {\n\n var a = indices[ i ];\n var b = indices[ i + 1 ];\n\n vStart.fromArray( positions, a * 3 );\n vEnd.fromArray( positions, b * 3 );\n\n var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n } else {\n\n for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {\n\n vStart.fromArray( positions, 3 * i );\n vEnd.fromArray( positions, 3 * i + 3 );\n\n var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var nbVertices = vertices.length;\n\n for ( var i = 0; i < nbVertices - 1; i += step ) {\n\n var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LineSegments( geometry, material ) {\n\n Line.call( this, geometry, material );\n\n this.type = 'LineSegments';\n\n }\n\n LineSegments.prototype = Object.assign( Object.create( Line.prototype ), {\n\n constructor: LineSegments,\n\n isLineSegments: true,\n\n computeLineDistances: ( function () {\n\n var start = new Vector3();\n var end = new Vector3();\n\n return function computeLineDistances() {\n\n var geometry = this.geometry;\n\n if ( geometry.isBufferGeometry ) {\n\n // we assume non-indexed geometry\n\n if ( geometry.index === null ) {\n\n var positionAttribute = geometry.attributes.position;\n var lineDistances = [];\n\n for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) {\n\n start.fromBufferAttribute( positionAttribute, i );\n end.fromBufferAttribute( positionAttribute, i + 1 );\n\n lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );\n\n }\n\n geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) );\n\n } else {\n\n console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var lineDistances = geometry.lineDistances;\n\n for ( var i = 0, l = vertices.length; i < l; i += 2 ) {\n\n start.copy( vertices[ i ] );\n end.copy( vertices[ i + 1 ] );\n\n lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );\n\n }\n\n }\n\n return this;\n\n };\n\n }() )\n\n } );\n\n /**\n * @author mgreter / http://github.com/mgreter\n */\n\n function LineLoop( geometry, material ) {\n\n Line.call( this, geometry, material );\n\n this.type = 'LineLoop';\n\n }\n\n LineLoop.prototype = Object.assign( Object.create( Line.prototype ), {\n\n constructor: LineLoop,\n\n isLineLoop: true,\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * size: <float>,\n * sizeAttenuation: <bool>\n * }\n */\n\n function PointsMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'PointsMaterial';\n\n this.color = new Color( 0xffffff );\n\n this.map = null;\n\n this.size = 1;\n this.sizeAttenuation = true;\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n PointsMaterial.prototype = Object.create( Material.prototype );\n PointsMaterial.prototype.constructor = PointsMaterial;\n\n PointsMaterial.prototype.isPointsMaterial = true;\n\n PointsMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.size = source.size;\n this.sizeAttenuation = source.sizeAttenuation;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Points( geometry, material ) {\n\n Object3D.call( this );\n\n this.type = 'Points';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } );\n\n }\n\n Points.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Points,\n\n isPoints: true,\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n return function raycast( raycaster, intersects ) {\n\n var object = this;\n var geometry = this.geometry;\n var matrixWorld = this.matrixWorld;\n var threshold = raycaster.params.Points.threshold;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n sphere.radius += threshold;\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n var localThresholdSq = localThreshold * localThreshold;\n var position = new Vector3();\n var intersectPoint = new Vector3();\n\n function testPoint( point, index ) {\n\n var rayPointDistanceSq = ray.distanceSqToPoint( point );\n\n if ( rayPointDistanceSq < localThresholdSq ) {\n\n ray.closestPointToPoint( point, intersectPoint );\n intersectPoint.applyMatrix4( matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return;\n\n intersects.push( {\n\n distance: distance,\n distanceToRay: Math.sqrt( rayPointDistanceSq ),\n point: intersectPoint.clone(),\n index: index,\n face: null,\n object: object\n\n } );\n\n }\n\n }\n\n if ( geometry.isBufferGeometry ) {\n\n var index = geometry.index;\n var attributes = geometry.attributes;\n var positions = attributes.position.array;\n\n if ( index !== null ) {\n\n var indices = index.array;\n\n for ( var i = 0, il = indices.length; i < il; i ++ ) {\n\n var a = indices[ i ];\n\n position.fromArray( positions, a * 3 );\n\n testPoint( position, a );\n\n }\n\n } else {\n\n for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {\n\n position.fromArray( positions, i * 3 );\n\n testPoint( position, i );\n\n }\n\n }\n\n } else {\n\n var vertices = geometry.vertices;\n\n for ( var i = 0, l = vertices.length; i < l; i ++ ) {\n\n testPoint( vertices[ i ], i );\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Group() {\n\n Object3D.call( this );\n\n this.type = 'Group';\n\n }\n\n Group.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Group,\n\n isGroup: true\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.generateMipmaps = false;\n\n }\n\n VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), {\n\n constructor: VideoTexture,\n\n isVideoTexture: true,\n\n update: function () {\n\n var video = this.image;\n\n if ( video.readyState >= video.HAVE_CURRENT_DATA ) {\n\n this.needsUpdate = true;\n\n }\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.image = { width: width, height: height };\n this.mipmaps = mipmaps;\n\n // no flipping for cube textures\n // (also flipping doesn't work for compressed textures )\n\n this.flipY = false;\n\n // can't generate mipmaps for compressed textures\n // mips must be embedded in DDS files\n\n this.generateMipmaps = false;\n\n }\n\n CompressedTexture.prototype = Object.create( Texture.prototype );\n CompressedTexture.prototype.constructor = CompressedTexture;\n\n CompressedTexture.prototype.isCompressedTexture = true;\n\n /**\n * @author Matt DesLauriers / @mattdesl\n * @author atix / arthursilber.de\n */\n\n function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {\n\n format = format !== undefined ? format : DepthFormat;\n\n if ( format !== DepthFormat && format !== DepthStencilFormat ) {\n\n throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );\n\n }\n\n if ( type === undefined && format === DepthFormat ) type = UnsignedShortType;\n if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type;\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.image = { width: width, height: height };\n\n this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n this.flipY = false;\n this.generateMipmaps = false;\n\n }\n\n DepthTexture.prototype = Object.create( Texture.prototype );\n DepthTexture.prototype.constructor = DepthTexture;\n DepthTexture.prototype.isDepthTexture = true;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WireframeGeometry( geometry ) {\n\n BufferGeometry.call( this );\n\n this.type = 'WireframeGeometry';\n\n // buffer\n\n var vertices = [];\n\n // helper variables\n\n var i, j, l, o, ol;\n var edge = [ 0, 0 ], edges = {}, e, edge1, edge2;\n var key, keys = [ 'a', 'b', 'c' ];\n var vertex;\n\n // different logic for Geometry and BufferGeometry\n\n if ( geometry && geometry.isGeometry ) {\n\n // create a data structure that contains all edges without duplicates\n\n var faces = geometry.faces;\n\n for ( i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( j = 0; j < 3; j ++ ) {\n\n edge1 = face[ keys[ j ] ];\n edge2 = face[ keys[ ( j + 1 ) % 3 ] ];\n edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n e = edges[ key ];\n\n vertex = geometry.vertices[ e.index1 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex = geometry.vertices[ e.index2 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n } else if ( geometry && geometry.isBufferGeometry ) {\n\n var position, indices, groups;\n var group, start, count;\n var index1, index2;\n\n vertex = new Vector3();\n\n if ( geometry.index !== null ) {\n\n // indexed BufferGeometry\n\n position = geometry.attributes.position;\n indices = geometry.index;\n groups = geometry.groups;\n\n if ( groups.length === 0 ) {\n\n groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];\n\n }\n\n // create a data structure that contains all eges without duplicates\n\n for ( o = 0, ol = groups.length; o < ol; ++ o ) {\n\n group = groups[ o ];\n\n start = group.start;\n count = group.count;\n\n for ( i = start, l = ( start + count ); i < l; i += 3 ) {\n\n for ( j = 0; j < 3; j ++ ) {\n\n edge1 = indices.getX( i + j );\n edge2 = indices.getX( i + ( j + 1 ) % 3 );\n edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };\n\n }\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n e = edges[ key ];\n\n vertex.fromBufferAttribute( position, e.index1 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex.fromBufferAttribute( position, e.index2 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n } else {\n\n // non-indexed BufferGeometry\n\n position = geometry.attributes.position;\n\n for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) {\n\n for ( j = 0; j < 3; j ++ ) {\n\n // three edges per triangle, an edge is represented as (index1, index2)\n // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)\n\n index1 = 3 * i + j;\n vertex.fromBufferAttribute( position, index1 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n index2 = 3 * i + ( ( j + 1 ) % 3 );\n vertex.fromBufferAttribute( position, index2 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n }\n\n }\n\n // build geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n }\n\n WireframeGeometry.prototype = Object.create( BufferGeometry.prototype );\n WireframeGeometry.prototype.constructor = WireframeGeometry;\n\n /**\n * @author zz85 / https://github.com/zz85\n * @author Mugen87 / https://github.com/Mugen87\n *\n * Parametric Surfaces Geometry\n * based on the brilliant article by @prideout http://prideout.net/blog/?p=44\n */\n\n // ParametricGeometry\n\n function ParametricGeometry( func, slices, stacks ) {\n\n Geometry.call( this );\n\n this.type = 'ParametricGeometry';\n\n this.parameters = {\n func: func,\n slices: slices,\n stacks: stacks\n };\n\n this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) );\n this.mergeVertices();\n\n }\n\n ParametricGeometry.prototype = Object.create( Geometry.prototype );\n ParametricGeometry.prototype.constructor = ParametricGeometry;\n\n // ParametricBufferGeometry\n\n function ParametricBufferGeometry( func, slices, stacks ) {\n\n BufferGeometry.call( this );\n\n this.type = 'ParametricBufferGeometry';\n\n this.parameters = {\n func: func,\n slices: slices,\n stacks: stacks\n };\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n var EPS = 0.00001;\n\n var normal = new Vector3();\n\n var p0 = new Vector3(), p1 = new Vector3();\n var pu = new Vector3(), pv = new Vector3();\n\n var i, j;\n\n // generate vertices, normals and uvs\n\n var sliceCount = slices + 1;\n\n for ( i = 0; i <= stacks; i ++ ) {\n\n var v = i / stacks;\n\n for ( j = 0; j <= slices; j ++ ) {\n\n var u = j / slices;\n\n // vertex\n\n func( u, v, p0 );\n vertices.push( p0.x, p0.y, p0.z );\n\n // normal\n\n // approximate tangent vectors via finite differences\n\n if ( u - EPS >= 0 ) {\n\n func( u - EPS, v, p1 );\n pu.subVectors( p0, p1 );\n\n } else {\n\n func( u + EPS, v, p1 );\n pu.subVectors( p1, p0 );\n\n }\n\n if ( v - EPS >= 0 ) {\n\n func( u, v - EPS, p1 );\n pv.subVectors( p0, p1 );\n\n } else {\n\n func( u, v + EPS, p1 );\n pv.subVectors( p1, p0 );\n\n }\n\n // cross product of tangent vectors returns surface normal\n\n normal.crossVectors( pu, pv ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, v );\n\n }\n\n }\n\n // generate indices\n\n for ( i = 0; i < stacks; i ++ ) {\n\n for ( j = 0; j < slices; j ++ ) {\n\n var a = i * sliceCount + j;\n var b = i * sliceCount + j + 1;\n var c = ( i + 1 ) * sliceCount + j + 1;\n var d = ( i + 1 ) * sliceCount + j;\n\n // faces one and two\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry;\n\n /**\n * @author clockworkgeek / https://github.com/clockworkgeek\n * @author timothypratley / https://github.com/timothypratley\n * @author WestLangley / http://github.com/WestLangley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // PolyhedronGeometry\n\n function PolyhedronGeometry( vertices, indices, radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'PolyhedronGeometry';\n\n this.parameters = {\n vertices: vertices,\n indices: indices,\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) );\n this.mergeVertices();\n\n }\n\n PolyhedronGeometry.prototype = Object.create( Geometry.prototype );\n PolyhedronGeometry.prototype.constructor = PolyhedronGeometry;\n\n // PolyhedronBufferGeometry\n\n function PolyhedronBufferGeometry( vertices, indices, radius, detail ) {\n\n BufferGeometry.call( this );\n\n this.type = 'PolyhedronBufferGeometry';\n\n this.parameters = {\n vertices: vertices,\n indices: indices,\n radius: radius,\n detail: detail\n };\n\n radius = radius || 1;\n detail = detail || 0;\n\n // default buffer data\n\n var vertexBuffer = [];\n var uvBuffer = [];\n\n // the subdivision creates the vertex buffer data\n\n subdivide( detail );\n\n // all vertices should lie on a conceptual sphere with a given radius\n\n appplyRadius( radius );\n\n // finally, create the uv data\n\n generateUVs();\n\n // build non-indexed geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );\n\n if ( detail === 0 ) {\n\n this.computeVertexNormals(); // flat normals\n\n } else {\n\n this.normalizeNormals(); // smooth normals\n\n }\n\n // helper functions\n\n function subdivide( detail ) {\n\n var a = new Vector3();\n var b = new Vector3();\n var c = new Vector3();\n\n // iterate over all faces and apply a subdivison with the given detail value\n\n for ( var i = 0; i < indices.length; i += 3 ) {\n\n // get the vertices of the face\n\n getVertexByIndex( indices[ i + 0 ], a );\n getVertexByIndex( indices[ i + 1 ], b );\n getVertexByIndex( indices[ i + 2 ], c );\n\n // perform subdivision\n\n subdivideFace( a, b, c, detail );\n\n }\n\n }\n\n function subdivideFace( a, b, c, detail ) {\n\n var cols = Math.pow( 2, detail );\n\n // we use this multidimensional array as a data structure for creating the subdivision\n\n var v = [];\n\n var i, j;\n\n // construct all of the vertices for this subdivision\n\n for ( i = 0; i <= cols; i ++ ) {\n\n v[ i ] = [];\n\n var aj = a.clone().lerp( c, i / cols );\n var bj = b.clone().lerp( c, i / cols );\n\n var rows = cols - i;\n\n for ( j = 0; j <= rows; j ++ ) {\n\n if ( j === 0 && i === cols ) {\n\n v[ i ][ j ] = aj;\n\n } else {\n\n v[ i ][ j ] = aj.clone().lerp( bj, j / rows );\n\n }\n\n }\n\n }\n\n // construct all of the faces\n\n for ( i = 0; i < cols; i ++ ) {\n\n for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {\n\n var k = Math.floor( j / 2 );\n\n if ( j % 2 === 0 ) {\n\n pushVertex( v[ i ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k ] );\n pushVertex( v[ i ][ k ] );\n\n } else {\n\n pushVertex( v[ i ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k ] );\n\n }\n\n }\n\n }\n\n }\n\n function appplyRadius( radius ) {\n\n var vertex = new Vector3();\n\n // iterate over the entire buffer and apply the radius to each vertex\n\n for ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n vertex.x = vertexBuffer[ i + 0 ];\n vertex.y = vertexBuffer[ i + 1 ];\n vertex.z = vertexBuffer[ i + 2 ];\n\n vertex.normalize().multiplyScalar( radius );\n\n vertexBuffer[ i + 0 ] = vertex.x;\n vertexBuffer[ i + 1 ] = vertex.y;\n vertexBuffer[ i + 2 ] = vertex.z;\n\n }\n\n }\n\n function generateUVs() {\n\n var vertex = new Vector3();\n\n for ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n vertex.x = vertexBuffer[ i + 0 ];\n vertex.y = vertexBuffer[ i + 1 ];\n vertex.z = vertexBuffer[ i + 2 ];\n\n var u = azimuth( vertex ) / 2 / Math.PI + 0.5;\n var v = inclination( vertex ) / Math.PI + 0.5;\n uvBuffer.push( u, 1 - v );\n\n }\n\n correctUVs();\n\n correctSeam();\n\n }\n\n function correctSeam() {\n\n // handle case when face straddles the seam, see #3269\n\n for ( var i = 0; i < uvBuffer.length; i += 6 ) {\n\n // uv data of a single face\n\n var x0 = uvBuffer[ i + 0 ];\n var x1 = uvBuffer[ i + 2 ];\n var x2 = uvBuffer[ i + 4 ];\n\n var max = Math.max( x0, x1, x2 );\n var min = Math.min( x0, x1, x2 );\n\n // 0.9 is somewhat arbitrary\n\n if ( max > 0.9 && min < 0.1 ) {\n\n if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;\n if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;\n if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;\n\n }\n\n }\n\n }\n\n function pushVertex( vertex ) {\n\n vertexBuffer.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n function getVertexByIndex( index, vertex ) {\n\n var stride = index * 3;\n\n vertex.x = vertices[ stride + 0 ];\n vertex.y = vertices[ stride + 1 ];\n vertex.z = vertices[ stride + 2 ];\n\n }\n\n function correctUVs() {\n\n var a = new Vector3();\n var b = new Vector3();\n var c = new Vector3();\n\n var centroid = new Vector3();\n\n var uvA = new Vector2();\n var uvB = new Vector2();\n var uvC = new Vector2();\n\n for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {\n\n a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );\n b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );\n c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );\n\n uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );\n uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );\n uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );\n\n centroid.copy( a ).add( b ).add( c ).divideScalar( 3 );\n\n var azi = azimuth( centroid );\n\n correctUV( uvA, j + 0, a, azi );\n correctUV( uvB, j + 2, b, azi );\n correctUV( uvC, j + 4, c, azi );\n\n }\n\n }\n\n function correctUV( uv, stride, vector, azimuth ) {\n\n if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {\n\n uvBuffer[ stride ] = uv.x - 1;\n\n }\n\n if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {\n\n uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;\n\n }\n\n }\n\n // Angle around the Y axis, counter-clockwise when looking from above.\n\n function azimuth( vector ) {\n\n return Math.atan2( vector.z, - vector.x );\n\n }\n\n\n // Angle above the XZ plane.\n\n function inclination( vector ) {\n\n return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );\n\n }\n\n }\n\n PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // TetrahedronGeometry\n\n function TetrahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'TetrahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n TetrahedronGeometry.prototype = Object.create( Geometry.prototype );\n TetrahedronGeometry.prototype.constructor = TetrahedronGeometry;\n\n // TetrahedronBufferGeometry\n\n function TetrahedronBufferGeometry( radius, detail ) {\n\n var vertices = [\n 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1\n ];\n\n var indices = [\n 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'TetrahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // OctahedronGeometry\n\n function OctahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'OctahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n OctahedronGeometry.prototype = Object.create( Geometry.prototype );\n OctahedronGeometry.prototype.constructor = OctahedronGeometry;\n\n // OctahedronBufferGeometry\n\n function OctahedronBufferGeometry( radius, detail ) {\n\n var vertices = [\n 1, 0, 0, - 1, 0, 0, 0, 1, 0,\n 0, - 1, 0, 0, 0, 1, 0, 0, - 1\n ];\n\n var indices = [\n 0, 2, 4, 0, 4, 3, 0, 3, 5,\n 0, 5, 2, 1, 2, 5, 1, 5, 3,\n 1, 3, 4, 1, 4, 2\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'OctahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // IcosahedronGeometry\n\n function IcosahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'IcosahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n IcosahedronGeometry.prototype = Object.create( Geometry.prototype );\n IcosahedronGeometry.prototype.constructor = IcosahedronGeometry;\n\n // IcosahedronBufferGeometry\n\n function IcosahedronBufferGeometry( radius, detail ) {\n\n var t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\n var vertices = [\n - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,\n 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,\n t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1\n ];\n\n var indices = [\n 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,\n 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,\n 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,\n 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'IcosahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry;\n\n /**\n * @author Abe Pazos / https://hamoid.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // DodecahedronGeometry\n\n function DodecahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'DodecahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n DodecahedronGeometry.prototype = Object.create( Geometry.prototype );\n DodecahedronGeometry.prototype.constructor = DodecahedronGeometry;\n\n // DodecahedronBufferGeometry\n\n function DodecahedronBufferGeometry( radius, detail ) {\n\n var t = ( 1 + Math.sqrt( 5 ) ) / 2;\n var r = 1 / t;\n\n var vertices = [\n\n // (±1, ±1, ±1)\n - 1, - 1, - 1, - 1, - 1, 1,\n - 1, 1, - 1, - 1, 1, 1,\n 1, - 1, - 1, 1, - 1, 1,\n 1, 1, - 1, 1, 1, 1,\n\n // (0, ±1/φ, ±φ)\n 0, - r, - t, 0, - r, t,\n 0, r, - t, 0, r, t,\n\n // (±1/φ, ±φ, 0)\n - r, - t, 0, - r, t, 0,\n r, - t, 0, r, t, 0,\n\n // (±φ, 0, ±1/φ)\n - t, 0, - r, t, 0, - r,\n - t, 0, r, t, 0, r\n ];\n\n var indices = [\n 3, 11, 7, 3, 7, 15, 3, 15, 13,\n 7, 19, 17, 7, 17, 6, 7, 6, 15,\n 17, 4, 8, 17, 8, 10, 17, 10, 6,\n 8, 0, 16, 8, 16, 2, 8, 2, 10,\n 0, 12, 1, 0, 1, 18, 0, 18, 16,\n 6, 10, 2, 6, 2, 13, 6, 13, 15,\n 2, 16, 18, 2, 18, 3, 2, 3, 13,\n 18, 1, 9, 18, 9, 11, 18, 11, 3,\n 4, 14, 12, 4, 12, 0, 4, 0, 8,\n 11, 9, 5, 11, 5, 19, 11, 19, 7,\n 19, 5, 14, 19, 14, 4, 19, 4, 17,\n 1, 12, 14, 1, 14, 5, 1, 5, 9\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'DodecahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry;\n\n /**\n * @author oosmoxiecode / https://github.com/oosmoxiecode\n * @author WestLangley / https://github.com/WestLangley\n * @author zz85 / https://github.com/zz85\n * @author miningold / https://github.com/miningold\n * @author jonobr1 / https://github.com/jonobr1\n * @author Mugen87 / https://github.com/Mugen87\n *\n */\n\n // TubeGeometry\n\n function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) {\n\n Geometry.call( this );\n\n this.type = 'TubeGeometry';\n\n this.parameters = {\n path: path,\n tubularSegments: tubularSegments,\n radius: radius,\n radialSegments: radialSegments,\n closed: closed\n };\n\n if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' );\n\n var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed );\n\n // expose internals\n\n this.tangents = bufferGeometry.tangents;\n this.normals = bufferGeometry.normals;\n this.binormals = bufferGeometry.binormals;\n\n // create geometry\n\n this.fromBufferGeometry( bufferGeometry );\n this.mergeVertices();\n\n }\n\n TubeGeometry.prototype = Object.create( Geometry.prototype );\n TubeGeometry.prototype.constructor = TubeGeometry;\n\n // TubeBufferGeometry\n\n function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TubeBufferGeometry';\n\n this.parameters = {\n path: path,\n tubularSegments: tubularSegments,\n radius: radius,\n radialSegments: radialSegments,\n closed: closed\n };\n\n tubularSegments = tubularSegments || 64;\n radius = radius || 1;\n radialSegments = radialSegments || 8;\n closed = closed || false;\n\n var frames = path.computeFrenetFrames( tubularSegments, closed );\n\n // expose internals\n\n this.tangents = frames.tangents;\n this.normals = frames.normals;\n this.binormals = frames.binormals;\n\n // helper variables\n\n var vertex = new Vector3();\n var normal = new Vector3();\n var uv = new Vector2();\n var P = new Vector3();\n\n var i, j;\n\n // buffer\n\n var vertices = [];\n var normals = [];\n var uvs = [];\n var indices = [];\n\n // create buffer data\n\n generateBufferData();\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // functions\n\n function generateBufferData() {\n\n for ( i = 0; i < tubularSegments; i ++ ) {\n\n generateSegment( i );\n\n }\n\n // if the geometry is not closed, generate the last row of vertices and normals\n // at the regular position on the given path\n //\n // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)\n\n generateSegment( ( closed === false ) ? tubularSegments : 0 );\n\n // uvs are generated in a separate function.\n // this makes it easy compute correct values for closed geometries\n\n generateUVs();\n\n // finally create faces\n\n generateIndices();\n\n }\n\n function generateSegment( i ) {\n\n // we use getPointAt to sample evenly distributed points from the given path\n\n P = path.getPointAt( i / tubularSegments, P );\n\n // retrieve corresponding normal and binormal\n\n var N = frames.normals[ i ];\n var B = frames.binormals[ i ];\n\n // generate normals and vertices for the current segment\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n var v = j / radialSegments * Math.PI * 2;\n\n var sin = Math.sin( v );\n var cos = - Math.cos( v );\n\n // normal\n\n normal.x = ( cos * N.x + sin * B.x );\n normal.y = ( cos * N.y + sin * B.y );\n normal.z = ( cos * N.z + sin * B.z );\n normal.normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // vertex\n\n vertex.x = P.x + radius * normal.x;\n vertex.y = P.y + radius * normal.y;\n vertex.z = P.z + radius * normal.z;\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n function generateIndices() {\n\n for ( j = 1; j <= tubularSegments; j ++ ) {\n\n for ( i = 1; i <= radialSegments; i ++ ) {\n\n var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n var b = ( radialSegments + 1 ) * j + ( i - 1 );\n var c = ( radialSegments + 1 ) * j + i;\n var d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n }\n\n function generateUVs() {\n\n for ( i = 0; i <= tubularSegments; i ++ ) {\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n uv.x = i / tubularSegments;\n uv.y = j / radialSegments;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n }\n\n }\n\n }\n\n TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TubeBufferGeometry.prototype.constructor = TubeBufferGeometry;\n\n /**\n * @author oosmoxiecode\n * @author Mugen87 / https://github.com/Mugen87\n *\n * based on http://www.blackpawn.com/texts/pqtorus/\n */\n\n // TorusKnotGeometry\n\n function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {\n\n Geometry.call( this );\n\n this.type = 'TorusKnotGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n tubularSegments: tubularSegments,\n radialSegments: radialSegments,\n p: p,\n q: q\n };\n\n if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );\n\n this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );\n this.mergeVertices();\n\n }\n\n TorusKnotGeometry.prototype = Object.create( Geometry.prototype );\n TorusKnotGeometry.prototype.constructor = TorusKnotGeometry;\n\n // TorusKnotBufferGeometry\n\n function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TorusKnotBufferGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n tubularSegments: tubularSegments,\n radialSegments: radialSegments,\n p: p,\n q: q\n };\n\n radius = radius || 1;\n tube = tube || 0.4;\n tubularSegments = Math.floor( tubularSegments ) || 64;\n radialSegments = Math.floor( radialSegments ) || 8;\n p = p || 2;\n q = q || 3;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var i, j;\n\n var vertex = new Vector3();\n var normal = new Vector3();\n\n var P1 = new Vector3();\n var P2 = new Vector3();\n\n var B = new Vector3();\n var T = new Vector3();\n var N = new Vector3();\n\n // generate vertices, normals and uvs\n\n for ( i = 0; i <= tubularSegments; ++ i ) {\n\n // the radian \"u\" is used to calculate the position on the torus curve of the current tubular segement\n\n var u = i / tubularSegments * p * Math.PI * 2;\n\n // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.\n // these points are used to create a special \"coordinate space\", which is necessary to calculate the correct vertex positions\n\n calculatePositionOnCurve( u, p, q, radius, P1 );\n calculatePositionOnCurve( u + 0.01, p, q, radius, P2 );\n\n // calculate orthonormal basis\n\n T.subVectors( P2, P1 );\n N.addVectors( P2, P1 );\n B.crossVectors( T, N );\n N.crossVectors( B, T );\n\n // normalize B, N. T can be ignored, we don't use it\n\n B.normalize();\n N.normalize();\n\n for ( j = 0; j <= radialSegments; ++ j ) {\n\n // now calculate the vertices. they are nothing more than an extrusion of the torus curve.\n // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.\n\n var v = j / radialSegments * Math.PI * 2;\n var cx = - tube * Math.cos( v );\n var cy = tube * Math.sin( v );\n\n // now calculate the final vertex position.\n // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve\n\n vertex.x = P1.x + ( cx * N.x + cy * B.x );\n vertex.y = P1.y + ( cx * N.y + cy * B.y );\n vertex.z = P1.z + ( cx * N.z + cy * B.z );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)\n\n normal.subVectors( vertex, P1 ).normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( i / tubularSegments );\n uvs.push( j / radialSegments );\n\n }\n\n }\n\n // generate indices\n\n for ( j = 1; j <= tubularSegments; j ++ ) {\n\n for ( i = 1; i <= radialSegments; i ++ ) {\n\n // indices\n\n var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n var b = ( radialSegments + 1 ) * j + ( i - 1 );\n var c = ( radialSegments + 1 ) * j + i;\n var d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // this function calculates the current position on the torus curve\n\n function calculatePositionOnCurve( u, p, q, radius, position ) {\n\n var cu = Math.cos( u );\n var su = Math.sin( u );\n var quOverP = q / p * u;\n var cs = Math.cos( quOverP );\n\n position.x = radius * ( 2 + cs ) * 0.5 * cu;\n position.y = radius * ( 2 + cs ) * su * 0.5;\n position.z = radius * Math.sin( quOverP ) * 0.5;\n\n }\n\n }\n\n TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry;\n\n /**\n * @author oosmoxiecode\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // TorusGeometry\n\n function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n Geometry.call( this );\n\n this.type = 'TorusGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n radialSegments: radialSegments,\n tubularSegments: tubularSegments,\n arc: arc\n };\n\n this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) );\n this.mergeVertices();\n\n }\n\n TorusGeometry.prototype = Object.create( Geometry.prototype );\n TorusGeometry.prototype.constructor = TorusGeometry;\n\n // TorusBufferGeometry\n\n function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TorusBufferGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n radialSegments: radialSegments,\n tubularSegments: tubularSegments,\n arc: arc\n };\n\n radius = radius || 1;\n tube = tube || 0.4;\n radialSegments = Math.floor( radialSegments ) || 8;\n tubularSegments = Math.floor( tubularSegments ) || 6;\n arc = arc || Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var center = new Vector3();\n var vertex = new Vector3();\n var normal = new Vector3();\n\n var j, i;\n\n // generate vertices, normals and uvs\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n for ( i = 0; i <= tubularSegments; i ++ ) {\n\n var u = i / tubularSegments * arc;\n var v = j / radialSegments * Math.PI * 2;\n\n // vertex\n\n vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );\n vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );\n vertex.z = tube * Math.sin( v );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n center.x = radius * Math.cos( u );\n center.y = radius * Math.sin( u );\n normal.subVectors( vertex, center ).normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( i / tubularSegments );\n uvs.push( j / radialSegments );\n\n }\n\n }\n\n // generate indices\n\n for ( j = 1; j <= radialSegments; j ++ ) {\n\n for ( i = 1; i <= tubularSegments; i ++ ) {\n\n // indices\n\n var a = ( tubularSegments + 1 ) * j + i - 1;\n var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;\n var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;\n var d = ( tubularSegments + 1 ) * j + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TorusBufferGeometry.prototype.constructor = TorusBufferGeometry;\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n * Port from https://github.com/mapbox/earcut (v2.1.2)\n */\n\n var Earcut = {\n\n triangulate: function ( data, holeIndices, dim ) {\n\n dim = dim || 2;\n\n var hasHoles = holeIndices && holeIndices.length,\n outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length,\n outerNode = linkedList( data, 0, outerLen, dim, true ),\n triangles = [];\n\n if ( ! outerNode ) return triangles;\n\n var minX, minY, maxX, maxY, x, y, invSize;\n\n if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );\n\n // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox\n\n if ( data.length > 80 * dim ) {\n\n minX = maxX = data[ 0 ];\n minY = maxY = data[ 1 ];\n\n for ( var i = dim; i < outerLen; i += dim ) {\n\n x = data[ i ];\n y = data[ i + 1 ];\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n\n }\n\n // minX, minY and invSize are later used to transform coords into integers for z-order calculation\n\n invSize = Math.max( maxX - minX, maxY - minY );\n invSize = invSize !== 0 ? 1 / invSize : 0;\n\n }\n\n earcutLinked( outerNode, triangles, dim, minX, minY, invSize );\n\n return triangles;\n\n }\n\n };\n\n // create a circular doubly linked list from polygon points in the specified winding order\n\n function linkedList( data, start, end, dim, clockwise ) {\n\n var i, last;\n\n if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) {\n\n for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n } else {\n\n for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n }\n\n if ( last && equals( last, last.next ) ) {\n\n removeNode( last );\n last = last.next;\n\n }\n\n return last;\n\n }\n\n // eliminate colinear or duplicate points\n\n function filterPoints( start, end ) {\n\n if ( ! start ) return start;\n if ( ! end ) end = start;\n\n var p = start, again;\n\n do {\n\n again = false;\n\n if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) {\n\n removeNode( p );\n p = end = p.prev;\n if ( p === p.next ) break;\n again = true;\n\n } else {\n\n p = p.next;\n\n }\n\n } while ( again || p !== end );\n\n return end;\n\n }\n\n // main ear slicing loop which triangulates a polygon (given as a linked list)\n\n function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {\n\n if ( ! ear ) return;\n\n // interlink polygon nodes in z-order\n\n if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );\n\n var stop = ear, prev, next;\n\n // iterate through ears, slicing them one by one\n\n while ( ear.prev !== ear.next ) {\n\n prev = ear.prev;\n next = ear.next;\n\n if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) {\n\n // cut off the triangle\n triangles.push( prev.i / dim );\n triangles.push( ear.i / dim );\n triangles.push( next.i / dim );\n\n removeNode( ear );\n\n // skipping the next vertice leads to less sliver triangles\n ear = next.next;\n stop = next.next;\n\n continue;\n\n }\n\n ear = next;\n\n // if we looped through the whole remaining polygon and can't find any more ears\n\n if ( ear === stop ) {\n\n // try filtering points and slicing again\n\n if ( ! pass ) {\n\n earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );\n\n // if this didn't work, try curing all small self-intersections locally\n\n } else if ( pass === 1 ) {\n\n ear = cureLocalIntersections( ear, triangles, dim );\n earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 );\n\n // as a last resort, try splitting the remaining polygon into two\n\n } else if ( pass === 2 ) {\n\n splitEarcut( ear, triangles, dim, minX, minY, invSize );\n\n }\n\n break;\n\n }\n\n }\n\n }\n\n // check whether a polygon node forms a valid ear with adjacent nodes\n\n function isEar( ear ) {\n\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n // now make sure we don't have other points inside the potential ear\n var p = ear.next.next;\n\n while ( p !== ear.prev ) {\n\n if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) {\n\n return false;\n\n }\n\n p = p.next;\n\n }\n\n return true;\n\n }\n\n function isEarHashed( ear, minX, minY, invSize ) {\n\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n // triangle bbox; min & max are calculated like this for speed\n\n var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ),\n minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ),\n maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ),\n maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y );\n\n // z-order range for the current triangle bbox;\n\n var minZ = zOrder( minTX, minTY, minX, minY, invSize ),\n maxZ = zOrder( maxTX, maxTY, minX, minY, invSize );\n\n // first look for points inside the triangle in increasing z-order\n\n var p = ear.nextZ;\n\n while ( p && p.z <= maxZ ) {\n\n if ( p !== ear.prev && p !== ear.next &&\n pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&\n area( p.prev, p, p.next ) >= 0 ) return false;\n p = p.nextZ;\n\n }\n\n // then look for points in decreasing z-order\n\n p = ear.prevZ;\n\n while ( p && p.z >= minZ ) {\n\n if ( p !== ear.prev && p !== ear.next &&\n pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&\n area( p.prev, p, p.next ) >= 0 ) return false;\n\n p = p.prevZ;\n\n }\n\n return true;\n\n }\n\n // go through all polygon nodes and cure small local self-intersections\n\n function cureLocalIntersections( start, triangles, dim ) {\n\n var p = start;\n\n do {\n\n var a = p.prev, b = p.next.next;\n\n if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {\n\n triangles.push( a.i / dim );\n triangles.push( p.i / dim );\n triangles.push( b.i / dim );\n\n // remove two nodes involved\n\n removeNode( p );\n removeNode( p.next );\n\n p = start = b;\n\n }\n\n p = p.next;\n\n } while ( p !== start );\n\n return p;\n\n }\n\n // try splitting polygon into two and triangulate them independently\n\n function splitEarcut( start, triangles, dim, minX, minY, invSize ) {\n\n // look for a valid diagonal that divides the polygon into two\n\n var a = start;\n\n do {\n\n var b = a.next.next;\n\n while ( b !== a.prev ) {\n\n if ( a.i !== b.i && isValidDiagonal( a, b ) ) {\n\n // split the polygon in two by the diagonal\n\n var c = splitPolygon( a, b );\n\n // filter colinear points around the cuts\n\n a = filterPoints( a, a.next );\n c = filterPoints( c, c.next );\n\n // run earcut on each half\n\n earcutLinked( a, triangles, dim, minX, minY, invSize );\n earcutLinked( c, triangles, dim, minX, minY, invSize );\n return;\n\n }\n\n b = b.next;\n\n }\n\n a = a.next;\n\n } while ( a !== start );\n\n }\n\n // link every hole into the outer loop, producing a single-ring polygon without holes\n\n function eliminateHoles( data, holeIndices, outerNode, dim ) {\n\n var queue = [], i, len, start, end, list;\n\n for ( i = 0, len = holeIndices.length; i < len; i ++ ) {\n\n start = holeIndices[ i ] * dim;\n end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length;\n list = linkedList( data, start, end, dim, false );\n if ( list === list.next ) list.steiner = true;\n queue.push( getLeftmost( list ) );\n\n }\n\n queue.sort( compareX );\n\n // process holes from left to right\n\n for ( i = 0; i < queue.length; i ++ ) {\n\n eliminateHole( queue[ i ], outerNode );\n outerNode = filterPoints( outerNode, outerNode.next );\n\n }\n\n return outerNode;\n\n }\n\n function compareX( a, b ) {\n\n return a.x - b.x;\n\n }\n\n // find a bridge between vertices that connects hole with an outer ring and and link it\n\n function eliminateHole( hole, outerNode ) {\n\n outerNode = findHoleBridge( hole, outerNode );\n\n if ( outerNode ) {\n\n var b = splitPolygon( outerNode, hole );\n\n filterPoints( b, b.next );\n\n }\n\n }\n\n // David Eberly's algorithm for finding a bridge between hole and outer polygon\n\n function findHoleBridge( hole, outerNode ) {\n\n var p = outerNode,\n hx = hole.x,\n hy = hole.y,\n qx = - Infinity,\n m;\n\n // find a segment intersected by a ray from the hole's leftmost point to the left;\n // segment's endpoint with lesser x will be potential connection point\n\n do {\n\n if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) {\n\n var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y );\n\n if ( x <= hx && x > qx ) {\n\n qx = x;\n\n if ( x === hx ) {\n\n if ( hy === p.y ) return p;\n if ( hy === p.next.y ) return p.next;\n\n }\n\n m = p.x < p.next.x ? p : p.next;\n\n }\n\n }\n\n p = p.next;\n\n } while ( p !== outerNode );\n\n if ( ! m ) return null;\n\n if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint\n\n // look for points inside the triangle of hole point, segment intersection and endpoint;\n // if there are no points found, we have a valid connection;\n // otherwise choose the point of the minimum angle with the ray as connection point\n\n var stop = m,\n mx = m.x,\n my = m.y,\n tanMin = Infinity,\n tan;\n\n p = m.next;\n\n while ( p !== stop ) {\n\n if ( hx >= p.x && p.x >= mx && hx !== p.x &&\n pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {\n\n tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential\n\n if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) {\n\n m = p;\n tanMin = tan;\n\n }\n\n }\n\n p = p.next;\n\n }\n\n return m;\n\n }\n\n // interlink polygon nodes in z-order\n\n function indexCurve( start, minX, minY, invSize ) {\n\n var p = start;\n\n do {\n\n if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize );\n p.prevZ = p.prev;\n p.nextZ = p.next;\n p = p.next;\n\n } while ( p !== start );\n\n p.prevZ.nextZ = null;\n p.prevZ = null;\n\n sortLinked( p );\n\n }\n\n // Simon Tatham's linked list merge sort algorithm\n // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html\n\n function sortLinked( list ) {\n\n var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;\n\n do {\n\n p = list;\n list = null;\n tail = null;\n numMerges = 0;\n\n while ( p ) {\n\n numMerges ++;\n q = p;\n pSize = 0;\n\n for ( i = 0; i < inSize; i ++ ) {\n\n pSize ++;\n q = q.nextZ;\n if ( ! q ) break;\n\n }\n\n qSize = inSize;\n\n while ( pSize > 0 || ( qSize > 0 && q ) ) {\n\n if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) {\n\n e = p;\n p = p.nextZ;\n pSize --;\n\n } else {\n\n e = q;\n q = q.nextZ;\n qSize --;\n\n }\n\n if ( tail ) tail.nextZ = e;\n else list = e;\n\n e.prevZ = tail;\n tail = e;\n\n }\n\n p = q;\n\n }\n\n tail.nextZ = null;\n inSize *= 2;\n\n } while ( numMerges > 1 );\n\n return list;\n\n }\n\n // z-order of a point given coords and inverse of the longer side of data bbox\n\n function zOrder( x, y, minX, minY, invSize ) {\n\n // coords are transformed into non-negative 15-bit integer range\n\n x = 32767 * ( x - minX ) * invSize;\n y = 32767 * ( y - minY ) * invSize;\n\n x = ( x | ( x << 8 ) ) & 0x00FF00FF;\n x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;\n x = ( x | ( x << 2 ) ) & 0x33333333;\n x = ( x | ( x << 1 ) ) & 0x55555555;\n\n y = ( y | ( y << 8 ) ) & 0x00FF00FF;\n y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;\n y = ( y | ( y << 2 ) ) & 0x33333333;\n y = ( y | ( y << 1 ) ) & 0x55555555;\n\n return x | ( y << 1 );\n\n }\n\n // find the leftmost node of a polygon ring\n\n function getLeftmost( start ) {\n\n var p = start, leftmost = start;\n\n do {\n\n if ( p.x < leftmost.x ) leftmost = p;\n p = p.next;\n\n } while ( p !== start );\n\n return leftmost;\n\n }\n\n // check if a point lies within a convex triangle\n\n function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {\n\n return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 &&\n ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&\n ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;\n\n }\n\n // check if a diagonal between two polygon nodes is valid (lies in polygon interior)\n\n function isValidDiagonal( a, b ) {\n\n return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) &&\n locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );\n\n }\n\n // signed area of a triangle\n\n function area( p, q, r ) {\n\n return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );\n\n }\n\n // check if two points are equal\n\n function equals( p1, p2 ) {\n\n return p1.x === p2.x && p1.y === p2.y;\n\n }\n\n // check if two segments intersect\n\n function intersects( p1, q1, p2, q2 ) {\n\n if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) ||\n ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true;\n\n return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 &&\n area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0;\n\n }\n\n // check if a polygon diagonal intersects any polygon segments\n\n function intersectsPolygon( a, b ) {\n\n var p = a;\n\n do {\n\n if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&\n intersects( p, p.next, a, b ) ) {\n\n return true;\n\n }\n\n p = p.next;\n\n } while ( p !== a );\n\n return false;\n\n }\n\n // check if a polygon diagonal is locally inside the polygon\n\n function locallyInside( a, b ) {\n\n return area( a.prev, a, a.next ) < 0 ?\n area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 :\n area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0;\n\n }\n\n // check if the middle point of a polygon diagonal is inside the polygon\n\n function middleInside( a, b ) {\n\n var p = a,\n inside = false,\n px = ( a.x + b.x ) / 2,\n py = ( a.y + b.y ) / 2;\n\n do {\n\n if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y &&\n ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) {\n\n inside = ! inside;\n\n }\n\n p = p.next;\n\n } while ( p !== a );\n\n return inside;\n\n }\n\n // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;\n // if one belongs to the outer ring and another to a hole, it merges it into a single ring\n\n function splitPolygon( a, b ) {\n\n var a2 = new Node( a.i, a.x, a.y ),\n b2 = new Node( b.i, b.x, b.y ),\n an = a.next,\n bp = b.prev;\n\n a.next = b;\n b.prev = a;\n\n a2.next = an;\n an.prev = a2;\n\n b2.next = a2;\n a2.prev = b2;\n\n bp.next = b2;\n b2.prev = bp;\n\n return b2;\n\n }\n\n // create a node and optionally link it with previous one (in a circular doubly linked list)\n\n function insertNode( i, x, y, last ) {\n\n var p = new Node( i, x, y );\n\n if ( ! last ) {\n\n p.prev = p;\n p.next = p;\n\n } else {\n\n p.next = last.next;\n p.prev = last;\n last.next.prev = p;\n last.next = p;\n\n }\n\n return p;\n\n }\n\n function removeNode( p ) {\n\n p.next.prev = p.prev;\n p.prev.next = p.next;\n\n if ( p.prevZ ) p.prevZ.nextZ = p.nextZ;\n if ( p.nextZ ) p.nextZ.prevZ = p.prevZ;\n\n }\n\n function Node( i, x, y ) {\n\n // vertice index in coordinates array\n this.i = i;\n\n // vertex coordinates\n this.x = x;\n this.y = y;\n\n // previous and next vertice nodes in a polygon ring\n this.prev = null;\n this.next = null;\n\n // z-order curve value\n this.z = null;\n\n // previous and next nodes in z-order\n this.prevZ = null;\n this.nextZ = null;\n\n // indicates whether this is a steiner point\n this.steiner = false;\n\n }\n\n function signedArea( data, start, end, dim ) {\n\n var sum = 0;\n\n for ( var i = start, j = end - dim; i < end; i += dim ) {\n\n sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );\n j = i;\n\n }\n\n return sum;\n\n }\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n */\n\n var ShapeUtils = {\n\n // calculate area of the contour polygon\n\n area: function ( contour ) {\n\n var n = contour.length;\n var a = 0.0;\n\n for ( var p = n - 1, q = 0; q < n; p = q ++ ) {\n\n a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;\n\n }\n\n return a * 0.5;\n\n },\n\n isClockWise: function ( pts ) {\n\n return ShapeUtils.area( pts ) < 0;\n\n },\n\n triangulateShape: function ( contour, holes ) {\n\n var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]\n var holeIndices = []; // array of hole indices\n var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]\n\n removeDupEndPts( contour );\n addContour( vertices, contour );\n\n //\n\n var holeIndex = contour.length;\n\n holes.forEach( removeDupEndPts );\n\n for ( var i = 0; i < holes.length; i ++ ) {\n\n holeIndices.push( holeIndex );\n holeIndex += holes[ i ].length;\n addContour( vertices, holes[ i ] );\n\n }\n\n //\n\n var triangles = Earcut.triangulate( vertices, holeIndices );\n\n //\n\n for ( var i = 0; i < triangles.length; i += 3 ) {\n\n faces.push( triangles.slice( i, i + 3 ) );\n\n }\n\n return faces;\n\n }\n\n };\n\n function removeDupEndPts( points ) {\n\n var l = points.length;\n\n if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {\n\n points.pop();\n\n }\n\n }\n\n function addContour( vertices, contour ) {\n\n for ( var i = 0; i < contour.length; i ++ ) {\n\n vertices.push( contour[ i ].x );\n vertices.push( contour[ i ].y );\n\n }\n\n }\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n * Creates extruded geometry from a path shape.\n *\n * parameters = {\n *\n * curveSegments: <int>, // number of points on the curves\n * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too\n * amount: <int>, // Depth to extrude the shape\n *\n * bevelEnabled: <bool>, // turn on bevel\n * bevelThickness: <float>, // how deep into the original shape bevel goes\n * bevelSize: <float>, // how far from shape outline is bevel\n * bevelSegments: <int>, // number of bevel layers\n *\n * extrudePath: <THREE.Curve> // curve to extrude shape along\n * frames: <Object> // containing arrays of tangents, normals, binormals\n *\n * UVGenerator: <Object> // object that provides UV generator functions\n *\n * }\n */\n\n // ExtrudeGeometry\n\n function ExtrudeGeometry( shapes, options ) {\n\n Geometry.call( this );\n\n this.type = 'ExtrudeGeometry';\n\n this.parameters = {\n shapes: shapes,\n options: options\n };\n\n this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) );\n this.mergeVertices();\n\n }\n\n ExtrudeGeometry.prototype = Object.create( Geometry.prototype );\n ExtrudeGeometry.prototype.constructor = ExtrudeGeometry;\n\n // ExtrudeBufferGeometry\n\n function ExtrudeBufferGeometry( shapes, options ) {\n\n if ( typeof ( shapes ) === \"undefined\" ) {\n\n return;\n\n }\n\n BufferGeometry.call( this );\n\n this.type = 'ExtrudeBufferGeometry';\n\n shapes = Array.isArray( shapes ) ? shapes : [ shapes ];\n\n this.addShapeList( shapes, options );\n\n this.computeVertexNormals();\n\n // can't really use automatic vertex normals\n // as then front and back sides get smoothed too\n // should do separate smoothing just for sides\n\n //this.computeVertexNormals();\n\n //console.log( \"took\", ( Date.now() - startTime ) );\n\n }\n\n ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry;\n\n ExtrudeBufferGeometry.prototype.getArrays = function () {\n\n var positionAttribute = this.getAttribute( \"position\" );\n var verticesArray = positionAttribute ? Array.prototype.slice.call( positionAttribute.array ) : [];\n\n var uvAttribute = this.getAttribute( \"uv\" );\n var uvArray = uvAttribute ? Array.prototype.slice.call( uvAttribute.array ) : [];\n\n var IndexAttribute = this.index;\n var indicesArray = IndexAttribute ? Array.prototype.slice.call( IndexAttribute.array ) : [];\n\n return {\n position: verticesArray,\n uv: uvArray,\n index: indicesArray\n };\n\n };\n\n ExtrudeBufferGeometry.prototype.addShapeList = function ( shapes, options ) {\n\n var sl = shapes.length;\n options.arrays = this.getArrays();\n\n for ( var s = 0; s < sl; s ++ ) {\n\n var shape = shapes[ s ];\n this.addShape( shape, options );\n\n }\n\n this.setIndex( options.arrays.index );\n this.addAttribute( 'position', new Float32BufferAttribute( options.arrays.position, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( options.arrays.uv, 2 ) );\n\n };\n\n ExtrudeBufferGeometry.prototype.addShape = function ( shape, options ) {\n\n var arrays = options.arrays ? options.arrays : this.getArrays();\n var verticesArray = arrays.position;\n var indicesArray = arrays.index;\n var uvArray = arrays.uv;\n\n var placeholder = [];\n\n\n var amount = options.amount !== undefined ? options.amount : 100;\n\n var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10\n var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8\n var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;\n\n var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false\n\n var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;\n\n var steps = options.steps !== undefined ? options.steps : 1;\n\n var extrudePath = options.extrudePath;\n var extrudePts, extrudeByPath = false;\n\n // Use default WorldUVGenerator if no UV generators are specified.\n var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator;\n\n var splineTube, binormal, normal, position2;\n if ( extrudePath ) {\n\n extrudePts = extrudePath.getSpacedPoints( steps );\n\n extrudeByPath = true;\n bevelEnabled = false; // bevels not supported for path extrusion\n\n // SETUP TNB variables\n\n // TODO1 - have a .isClosed in spline?\n\n splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false );\n\n // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);\n\n binormal = new Vector3();\n normal = new Vector3();\n position2 = new Vector3();\n\n }\n\n // Safeguards if bevels are not enabled\n\n if ( ! bevelEnabled ) {\n\n bevelSegments = 0;\n bevelThickness = 0;\n bevelSize = 0;\n\n }\n\n // Variables initialization\n\n var ahole, h, hl; // looping of holes\n var scope = this;\n\n var shapePoints = shape.extractPoints( curveSegments );\n\n var vertices = shapePoints.shape;\n var holes = shapePoints.holes;\n\n var reverse = ! ShapeUtils.isClockWise( vertices );\n\n if ( reverse ) {\n\n vertices = vertices.reverse();\n\n // Maybe we should also check if holes are in the opposite direction, just to be safe ...\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n if ( ShapeUtils.isClockWise( ahole ) ) {\n\n holes[ h ] = ahole.reverse();\n\n }\n\n }\n\n }\n\n\n var faces = ShapeUtils.triangulateShape( vertices, holes );\n\n /* Vertices */\n\n var contour = vertices; // vertices has all points but contour has only points of circumference\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n vertices = vertices.concat( ahole );\n\n }\n\n\n function scalePt2( pt, vec, size ) {\n\n if ( ! vec ) console.error( \"THREE.ExtrudeGeometry: vec does not exist\" );\n\n return vec.clone().multiplyScalar( size ).add( pt );\n\n }\n\n var b, bs, t, z,\n vert, vlen = vertices.length,\n face, flen = faces.length;\n\n\n // Find directions for point movement\n\n\n function getBevelVec( inPt, inPrev, inNext ) {\n\n // computes for inPt the corresponding point inPt' on a new contour\n // shifted by 1 unit (length of normalized vector) to the left\n // if we walk along contour clockwise, this new contour is outside the old one\n //\n // inPt' is the intersection of the two lines parallel to the two\n // adjacent edges of inPt at a distance of 1 unit on the left side.\n\n var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt\n\n // good reading for geometry algorithms (here: line-line intersection)\n // http://geomalgorithms.com/a05-_intersect-1.html\n\n var v_prev_x = inPt.x - inPrev.x,\n v_prev_y = inPt.y - inPrev.y;\n var v_next_x = inNext.x - inPt.x,\n v_next_y = inNext.y - inPt.y;\n\n var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );\n\n // check for collinear edges\n var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n if ( Math.abs( collinear0 ) > Number.EPSILON ) {\n\n // not collinear\n\n // length of vectors for normalizing\n\n var v_prev_len = Math.sqrt( v_prev_lensq );\n var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );\n\n // shift adjacent points by unit vectors to the left\n\n var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );\n var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );\n\n var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );\n var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );\n\n // scaling factor for v_prev to intersection point\n\n var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -\n ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /\n ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n // vector from inPt to intersection point\n\n v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );\n v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );\n\n // Don't normalize!, otherwise sharp corners become ugly\n // but prevent crazy spikes\n var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );\n if ( v_trans_lensq <= 2 ) {\n\n return new Vector2( v_trans_x, v_trans_y );\n\n } else {\n\n shrink_by = Math.sqrt( v_trans_lensq / 2 );\n\n }\n\n } else {\n\n // handle special case of collinear edges\n\n var direction_eq = false; // assumes: opposite\n if ( v_prev_x > Number.EPSILON ) {\n\n if ( v_next_x > Number.EPSILON ) {\n\n direction_eq = true;\n\n }\n\n } else {\n\n if ( v_prev_x < - Number.EPSILON ) {\n\n if ( v_next_x < - Number.EPSILON ) {\n\n direction_eq = true;\n\n }\n\n } else {\n\n if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {\n\n direction_eq = true;\n\n }\n\n }\n\n }\n\n if ( direction_eq ) {\n\n // console.log(\"Warning: lines are a straight sequence\");\n v_trans_x = - v_prev_y;\n v_trans_y = v_prev_x;\n shrink_by = Math.sqrt( v_prev_lensq );\n\n } else {\n\n // console.log(\"Warning: lines are a straight spike\");\n v_trans_x = v_prev_x;\n v_trans_y = v_prev_y;\n shrink_by = Math.sqrt( v_prev_lensq / 2 );\n\n }\n\n }\n\n return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );\n\n }\n\n\n var contourMovements = [];\n\n for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n if ( j === il ) j = 0;\n if ( k === il ) k = 0;\n\n // (j)---(i)---(k)\n // console.log('i,j,k', i, j , k)\n\n contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );\n\n }\n\n var holesMovements = [],\n oneHoleMovements, verticesMovements = contourMovements.concat();\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n oneHoleMovements = [];\n\n for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n if ( j === il ) j = 0;\n if ( k === il ) k = 0;\n\n // (j)---(i)---(k)\n oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );\n\n }\n\n holesMovements.push( oneHoleMovements );\n verticesMovements = verticesMovements.concat( oneHoleMovements );\n\n }\n\n\n // Loop bevelSegments, 1 for the front, 1 for the back\n\n for ( b = 0; b < bevelSegments; b ++ ) {\n\n //for ( b = bevelSegments; b > 0; b -- ) {\n\n t = b / bevelSegments;\n z = bevelThickness * Math.cos( t * Math.PI / 2 );\n bs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n // contract shape\n\n for ( i = 0, il = contour.length; i < il; i ++ ) {\n\n vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\n v( vert.x, vert.y, - z );\n\n }\n\n // expand holes\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n oneHoleMovements = holesMovements[ h ];\n\n for ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n v( vert.x, vert.y, - z );\n\n }\n\n }\n\n }\n\n bs = bevelSize;\n\n // Back facing vertices\n\n for ( i = 0; i < vlen; i ++ ) {\n\n vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, 0 );\n\n } else {\n\n // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );\n\n normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );\n binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );\n\n position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );\n\n v( position2.x, position2.y, position2.z );\n\n }\n\n }\n\n // Add stepped vertices...\n // Including front facing vertices\n\n var s;\n\n for ( s = 1; s <= steps; s ++ ) {\n\n for ( i = 0; i < vlen; i ++ ) {\n\n vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, amount / steps * s );\n\n } else {\n\n // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );\n\n normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );\n binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );\n\n position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );\n\n v( position2.x, position2.y, position2.z );\n\n }\n\n }\n\n }\n\n\n // Add bevel segments planes\n\n //for ( b = 1; b <= bevelSegments; b ++ ) {\n for ( b = bevelSegments - 1; b >= 0; b -- ) {\n\n t = b / bevelSegments;\n z = bevelThickness * Math.cos( t * Math.PI / 2 );\n bs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n // contract shape\n\n for ( i = 0, il = contour.length; i < il; i ++ ) {\n\n vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n v( vert.x, vert.y, amount + z );\n\n }\n\n // expand holes\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n oneHoleMovements = holesMovements[ h ];\n\n for ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, amount + z );\n\n } else {\n\n v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );\n\n }\n\n }\n\n }\n\n }\n\n /* Faces */\n\n // Top and bottom faces\n\n buildLidFaces();\n\n // Sides faces\n\n buildSideFaces();\n\n\n ///// Internal functions\n\n function buildLidFaces() {\n\n var start = verticesArray.length / 3;\n\n if ( bevelEnabled ) {\n\n var layer = 0; // steps + 1\n var offset = vlen * layer;\n\n // Bottom faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );\n\n }\n\n layer = steps + bevelSegments * 2;\n offset = vlen * layer;\n\n // Top faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );\n\n }\n\n } else {\n\n // Bottom faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 2 ], face[ 1 ], face[ 0 ] );\n\n }\n\n // Top faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );\n\n }\n\n }\n\n scope.addGroup( start, verticesArray.length / 3 - start, 0 );\n\n }\n\n // Create faces for the z-sides of the shape\n\n function buildSideFaces() {\n\n var start = verticesArray.length / 3;\n var layeroffset = 0;\n sidewalls( contour, layeroffset );\n layeroffset += contour.length;\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n sidewalls( ahole, layeroffset );\n\n //, true\n layeroffset += ahole.length;\n\n }\n\n\n scope.addGroup( start, verticesArray.length / 3 - start, 1 );\n\n\n }\n\n function sidewalls( contour, layeroffset ) {\n\n var j, k;\n i = contour.length;\n\n while ( -- i >= 0 ) {\n\n j = i;\n k = i - 1;\n if ( k < 0 ) k = contour.length - 1;\n\n //console.log('b', i,j, i-1, k,vertices.length);\n\n var s = 0,\n sl = steps + bevelSegments * 2;\n\n for ( s = 0; s < sl; s ++ ) {\n\n var slen1 = vlen * s;\n var slen2 = vlen * ( s + 1 );\n\n var a = layeroffset + j + slen1,\n b = layeroffset + k + slen1,\n c = layeroffset + k + slen2,\n d = layeroffset + j + slen2;\n\n f4( a, b, c, d );\n\n }\n\n }\n\n }\n\n function v( x, y, z ) {\n\n placeholder.push( x );\n placeholder.push( y );\n placeholder.push( z );\n\n }\n\n\n function f3( a, b, c ) {\n\n addVertex( a );\n addVertex( b );\n addVertex( c );\n\n var nextIndex = verticesArray.length / 3;\n var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n addUV( uvs[ 0 ] );\n addUV( uvs[ 1 ] );\n addUV( uvs[ 2 ] );\n\n }\n\n function f4( a, b, c, d ) {\n\n addVertex( a );\n addVertex( b );\n addVertex( d );\n\n addVertex( b );\n addVertex( c );\n addVertex( d );\n\n\n var nextIndex = verticesArray.length / 3;\n var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n addUV( uvs[ 0 ] );\n addUV( uvs[ 1 ] );\n addUV( uvs[ 3 ] );\n\n addUV( uvs[ 1 ] );\n addUV( uvs[ 2 ] );\n addUV( uvs[ 3 ] );\n\n }\n\n function addVertex( index ) {\n\n indicesArray.push( verticesArray.length / 3 );\n verticesArray.push( placeholder[ index * 3 + 0 ] );\n verticesArray.push( placeholder[ index * 3 + 1 ] );\n verticesArray.push( placeholder[ index * 3 + 2 ] );\n\n }\n\n\n function addUV( vector2 ) {\n\n uvArray.push( vector2.x );\n uvArray.push( vector2.y );\n\n }\n\n if ( ! options.arrays ) {\n\n this.setIndex( indicesArray );\n this.addAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );\n\n }\n\n };\n\n ExtrudeGeometry.WorldUVGenerator = {\n\n generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {\n\n var a_x = vertices[ indexA * 3 ];\n var a_y = vertices[ indexA * 3 + 1 ];\n var b_x = vertices[ indexB * 3 ];\n var b_y = vertices[ indexB * 3 + 1 ];\n var c_x = vertices[ indexC * 3 ];\n var c_y = vertices[ indexC * 3 + 1 ];\n\n return [\n new Vector2( a_x, a_y ),\n new Vector2( b_x, b_y ),\n new Vector2( c_x, c_y )\n ];\n\n },\n\n generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {\n\n var a_x = vertices[ indexA * 3 ];\n var a_y = vertices[ indexA * 3 + 1 ];\n var a_z = vertices[ indexA * 3 + 2 ];\n var b_x = vertices[ indexB * 3 ];\n var b_y = vertices[ indexB * 3 + 1 ];\n var b_z = vertices[ indexB * 3 + 2 ];\n var c_x = vertices[ indexC * 3 ];\n var c_y = vertices[ indexC * 3 + 1 ];\n var c_z = vertices[ indexC * 3 + 2 ];\n var d_x = vertices[ indexD * 3 ];\n var d_y = vertices[ indexD * 3 + 1 ];\n var d_z = vertices[ indexD * 3 + 2 ];\n\n if ( Math.abs( a_y - b_y ) < 0.01 ) {\n\n return [\n new Vector2( a_x, 1 - a_z ),\n new Vector2( b_x, 1 - b_z ),\n new Vector2( c_x, 1 - c_z ),\n new Vector2( d_x, 1 - d_z )\n ];\n\n } else {\n\n return [\n new Vector2( a_y, 1 - a_z ),\n new Vector2( b_y, 1 - b_z ),\n new Vector2( c_y, 1 - c_z ),\n new Vector2( d_y, 1 - d_z )\n ];\n\n }\n\n }\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author alteredq / http://alteredqualia.com/\n *\n * Text = 3D Text\n *\n * parameters = {\n * font: <THREE.Font>, // font\n *\n * size: <float>, // size of the text\n * height: <float>, // thickness to extrude text\n * curveSegments: <int>, // number of points on the curves\n *\n * bevelEnabled: <bool>, // turn on bevel\n * bevelThickness: <float>, // how deep into text bevel goes\n * bevelSize: <float> // how far from text outline is bevel\n * }\n */\n\n // TextGeometry\n\n function TextGeometry( text, parameters ) {\n\n Geometry.call( this );\n\n this.type = 'TextGeometry';\n\n this.parameters = {\n text: text,\n parameters: parameters\n };\n\n this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) );\n this.mergeVertices();\n\n }\n\n TextGeometry.prototype = Object.create( Geometry.prototype );\n TextGeometry.prototype.constructor = TextGeometry;\n\n // TextBufferGeometry\n\n function TextBufferGeometry( text, parameters ) {\n\n parameters = parameters || {};\n\n var font = parameters.font;\n\n if ( ! ( font && font.isFont ) ) {\n\n console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );\n return new Geometry();\n\n }\n\n var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments );\n\n // translate parameters to ExtrudeGeometry API\n\n parameters.amount = parameters.height !== undefined ? parameters.height : 50;\n\n // defaults\n\n if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;\n if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;\n if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;\n\n ExtrudeBufferGeometry.call( this, shapes, parameters );\n\n this.type = 'TextBufferGeometry';\n\n }\n\n TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype );\n TextBufferGeometry.prototype.constructor = TextBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author benaadams / https://twitter.com/ben_a_adams\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // SphereGeometry\n\n function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'SphereGeometry';\n\n this.parameters = {\n radius: radius,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n phiStart: phiStart,\n phiLength: phiLength,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n SphereGeometry.prototype = Object.create( Geometry.prototype );\n SphereGeometry.prototype.constructor = SphereGeometry;\n\n // SphereBufferGeometry\n\n function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'SphereBufferGeometry';\n\n this.parameters = {\n radius: radius,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n phiStart: phiStart,\n phiLength: phiLength,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n radius = radius || 1;\n\n widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );\n heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );\n\n phiStart = phiStart !== undefined ? phiStart : 0;\n phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;\n\n var thetaEnd = thetaStart + thetaLength;\n\n var ix, iy;\n\n var index = 0;\n var grid = [];\n\n var vertex = new Vector3();\n var normal = new Vector3();\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy <= heightSegments; iy ++ ) {\n\n var verticesRow = [];\n\n var v = iy / heightSegments;\n\n for ( ix = 0; ix <= widthSegments; ix ++ ) {\n\n var u = ix / widthSegments;\n\n // vertex\n\n vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n vertex.y = radius * Math.cos( thetaStart + v * thetaLength );\n vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normal.set( vertex.x, vertex.y, vertex.z ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, 1 - v );\n\n verticesRow.push( index ++ );\n\n }\n\n grid.push( verticesRow );\n\n }\n\n // indices\n\n for ( iy = 0; iy < heightSegments; iy ++ ) {\n\n for ( ix = 0; ix < widthSegments; ix ++ ) {\n\n var a = grid[ iy ][ ix + 1 ];\n var b = grid[ iy ][ ix ];\n var c = grid[ iy + 1 ][ ix ];\n var d = grid[ iy + 1 ][ ix + 1 ];\n\n if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );\n if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n SphereBufferGeometry.prototype.constructor = SphereBufferGeometry;\n\n /**\n * @author Kaleb Murphy\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // RingGeometry\n\n function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'RingGeometry';\n\n this.parameters = {\n innerRadius: innerRadius,\n outerRadius: outerRadius,\n thetaSegments: thetaSegments,\n phiSegments: phiSegments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n RingGeometry.prototype = Object.create( Geometry.prototype );\n RingGeometry.prototype.constructor = RingGeometry;\n\n // RingBufferGeometry\n\n function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'RingBufferGeometry';\n\n this.parameters = {\n innerRadius: innerRadius,\n outerRadius: outerRadius,\n thetaSegments: thetaSegments,\n phiSegments: phiSegments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n innerRadius = innerRadius || 0.5;\n outerRadius = outerRadius || 1;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;\n phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // some helper variables\n\n var segment;\n var radius = innerRadius;\n var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );\n var vertex = new Vector3();\n var uv = new Vector2();\n var j, i;\n\n // generate vertices, normals and uvs\n\n for ( j = 0; j <= phiSegments; j ++ ) {\n\n for ( i = 0; i <= thetaSegments; i ++ ) {\n\n // values are generate from the inside of the ring to the outside\n\n segment = thetaStart + i / thetaSegments * thetaLength;\n\n // vertex\n\n vertex.x = radius * Math.cos( segment );\n vertex.y = radius * Math.sin( segment );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, 0, 1 );\n\n // uv\n\n uv.x = ( vertex.x / outerRadius + 1 ) / 2;\n uv.y = ( vertex.y / outerRadius + 1 ) / 2;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n // increase the radius for next row of vertices\n\n radius += radiusStep;\n\n }\n\n // indices\n\n for ( j = 0; j < phiSegments; j ++ ) {\n\n var thetaSegmentLevel = j * ( thetaSegments + 1 );\n\n for ( i = 0; i < thetaSegments; i ++ ) {\n\n segment = i + thetaSegmentLevel;\n\n var a = segment;\n var b = segment + thetaSegments + 1;\n var c = segment + thetaSegments + 2;\n var d = segment + 1;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n RingBufferGeometry.prototype.constructor = RingBufferGeometry;\n\n /**\n * @author astrodud / http://astrodud.isgreat.org/\n * @author zz85 / https://github.com/zz85\n * @author bhouston / http://clara.io\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // LatheGeometry\n\n function LatheGeometry( points, segments, phiStart, phiLength ) {\n\n Geometry.call( this );\n\n this.type = 'LatheGeometry';\n\n this.parameters = {\n points: points,\n segments: segments,\n phiStart: phiStart,\n phiLength: phiLength\n };\n\n this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) );\n this.mergeVertices();\n\n }\n\n LatheGeometry.prototype = Object.create( Geometry.prototype );\n LatheGeometry.prototype.constructor = LatheGeometry;\n\n // LatheBufferGeometry\n\n function LatheBufferGeometry( points, segments, phiStart, phiLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'LatheBufferGeometry';\n\n this.parameters = {\n points: points,\n segments: segments,\n phiStart: phiStart,\n phiLength: phiLength\n };\n\n segments = Math.floor( segments ) || 12;\n phiStart = phiStart || 0;\n phiLength = phiLength || Math.PI * 2;\n\n // clamp phiLength so it's in range of [ 0, 2PI ]\n\n phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 );\n\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var uvs = [];\n\n // helper variables\n\n var base;\n var inverseSegments = 1.0 / segments;\n var vertex = new Vector3();\n var uv = new Vector2();\n var i, j;\n\n // generate vertices and uvs\n\n for ( i = 0; i <= segments; i ++ ) {\n\n var phi = phiStart + i * inverseSegments * phiLength;\n\n var sin = Math.sin( phi );\n var cos = Math.cos( phi );\n\n for ( j = 0; j <= ( points.length - 1 ); j ++ ) {\n\n // vertex\n\n vertex.x = points[ j ].x * sin;\n vertex.y = points[ j ].y;\n vertex.z = points[ j ].x * cos;\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // uv\n\n uv.x = i / segments;\n uv.y = j / ( points.length - 1 );\n\n uvs.push( uv.x, uv.y );\n\n\n }\n\n }\n\n // indices\n\n for ( i = 0; i < segments; i ++ ) {\n\n for ( j = 0; j < ( points.length - 1 ); j ++ ) {\n\n base = j + i * points.length;\n\n var a = base;\n var b = base + points.length;\n var c = base + points.length + 1;\n var d = base + 1;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // generate normals\n\n this.computeVertexNormals();\n\n // if the geometry is closed, we need to average the normals along the seam.\n // because the corresponding vertices are identical (but still have different UVs).\n\n if ( phiLength === Math.PI * 2 ) {\n\n var normals = this.attributes.normal.array;\n var n1 = new Vector3();\n var n2 = new Vector3();\n var n = new Vector3();\n\n // this is the buffer offset for the last line of vertices\n\n base = segments * points.length * 3;\n\n for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) {\n\n // select the normal of the vertex in the first line\n\n n1.x = normals[ j + 0 ];\n n1.y = normals[ j + 1 ];\n n1.z = normals[ j + 2 ];\n\n // select the normal of the vertex in the last line\n\n n2.x = normals[ base + j + 0 ];\n n2.y = normals[ base + j + 1 ];\n n2.z = normals[ base + j + 2 ];\n\n // average normals\n\n n.addVectors( n1, n2 ).normalize();\n\n // assign the new values to both normals\n\n normals[ j + 0 ] = normals[ base + j + 0 ] = n.x;\n normals[ j + 1 ] = normals[ base + j + 1 ] = n.y;\n normals[ j + 2 ] = normals[ base + j + 2 ] = n.z;\n\n }\n\n }\n\n }\n\n LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n LatheBufferGeometry.prototype.constructor = LatheBufferGeometry;\n\n /**\n * @author jonobr1 / http://jonobr1.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // ShapeGeometry\n\n function ShapeGeometry( shapes, curveSegments ) {\n\n Geometry.call( this );\n\n this.type = 'ShapeGeometry';\n\n if ( typeof curveSegments === 'object' ) {\n\n console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' );\n\n curveSegments = curveSegments.curveSegments;\n\n }\n\n this.parameters = {\n shapes: shapes,\n curveSegments: curveSegments\n };\n\n this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) );\n this.mergeVertices();\n\n }\n\n ShapeGeometry.prototype = Object.create( Geometry.prototype );\n ShapeGeometry.prototype.constructor = ShapeGeometry;\n\n ShapeGeometry.prototype.toJSON = function () {\n\n var data = Geometry.prototype.toJSON.call( this );\n\n var shapes = this.parameters.shapes;\n\n return toJSON( shapes, data );\n\n };\n\n // ShapeBufferGeometry\n\n function ShapeBufferGeometry( shapes, curveSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'ShapeBufferGeometry';\n\n this.parameters = {\n shapes: shapes,\n curveSegments: curveSegments\n };\n\n curveSegments = curveSegments || 12;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var groupStart = 0;\n var groupCount = 0;\n\n // allow single and array values for \"shapes\" parameter\n\n if ( Array.isArray( shapes ) === false ) {\n\n addShape( shapes );\n\n } else {\n\n for ( var i = 0; i < shapes.length; i ++ ) {\n\n addShape( shapes[ i ] );\n\n this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support\n\n groupStart += groupCount;\n groupCount = 0;\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\n // helper functions\n\n function addShape( shape ) {\n\n var i, l, shapeHole;\n\n var indexOffset = vertices.length / 3;\n var points = shape.extractPoints( curveSegments );\n\n var shapeVertices = points.shape;\n var shapeHoles = points.holes;\n\n // check direction of vertices\n\n if ( ShapeUtils.isClockWise( shapeVertices ) === false ) {\n\n shapeVertices = shapeVertices.reverse();\n\n // also check if holes are in the opposite direction\n\n for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n shapeHole = shapeHoles[ i ];\n\n if ( ShapeUtils.isClockWise( shapeHole ) === true ) {\n\n shapeHoles[ i ] = shapeHole.reverse();\n\n }\n\n }\n\n }\n\n var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles );\n\n // join vertices of inner and outer paths to a single array\n\n for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n shapeHole = shapeHoles[ i ];\n shapeVertices = shapeVertices.concat( shapeHole );\n\n }\n\n // vertices, normals, uvs\n\n for ( i = 0, l = shapeVertices.length; i < l; i ++ ) {\n\n var vertex = shapeVertices[ i ];\n\n vertices.push( vertex.x, vertex.y, 0 );\n normals.push( 0, 0, 1 );\n uvs.push( vertex.x, vertex.y ); // world uvs\n\n }\n\n // incides\n\n for ( i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n var a = face[ 0 ] + indexOffset;\n var b = face[ 1 ] + indexOffset;\n var c = face[ 2 ] + indexOffset;\n\n indices.push( a, b, c );\n groupCount += 3;\n\n }\n\n }\n\n }\n\n ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry;\n\n ShapeBufferGeometry.prototype.toJSON = function () {\n\n var data = BufferGeometry.prototype.toJSON.call( this );\n\n var shapes = this.parameters.shapes;\n\n return toJSON( shapes, data );\n\n };\n\n //\n\n function toJSON( shapes, data ) {\n\n data.shapes = [];\n\n if ( Array.isArray( shapes ) ) {\n\n for ( var i = 0, l = shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ i ];\n\n data.shapes.push( shape.uuid );\n\n }\n\n } else {\n\n data.shapes.push( shapes.uuid );\n\n }\n\n return data;\n\n }\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function EdgesGeometry( geometry, thresholdAngle ) {\n\n BufferGeometry.call( this );\n\n this.type = 'EdgesGeometry';\n\n this.parameters = {\n thresholdAngle: thresholdAngle\n };\n\n thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;\n\n // buffer\n\n var vertices = [];\n\n // helper variables\n\n var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle );\n var edge = [ 0, 0 ], edges = {}, edge1, edge2;\n var key, keys = [ 'a', 'b', 'c' ];\n\n // prepare source geometry\n\n var geometry2;\n\n if ( geometry.isBufferGeometry ) {\n\n geometry2 = new Geometry();\n geometry2.fromBufferGeometry( geometry );\n\n } else {\n\n geometry2 = geometry.clone();\n\n }\n\n geometry2.mergeVertices();\n geometry2.computeFaceNormals();\n\n var sourceVertices = geometry2.vertices;\n var faces = geometry2.faces;\n\n // now create a data structure where each entry represents an edge with its adjoining faces\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( var j = 0; j < 3; j ++ ) {\n\n edge1 = face[ keys[ j ] ];\n edge2 = face[ keys[ ( j + 1 ) % 3 ] ];\n edge[ 0 ] = Math.min( edge1, edge2 );\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined };\n\n } else {\n\n edges[ key ].face2 = i;\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n var e = edges[ key ];\n\n // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.\n\n if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) {\n\n var vertex = sourceVertices[ e.index1 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex = sourceVertices[ e.index2 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n // build geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n }\n\n EdgesGeometry.prototype = Object.create( BufferGeometry.prototype );\n EdgesGeometry.prototype.constructor = EdgesGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // CylinderGeometry\n\n function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'CylinderGeometry';\n\n this.parameters = {\n radiusTop: radiusTop,\n radiusBottom: radiusBottom,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n CylinderGeometry.prototype = Object.create( Geometry.prototype );\n CylinderGeometry.prototype.constructor = CylinderGeometry;\n\n // CylinderBufferGeometry\n\n function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'CylinderBufferGeometry';\n\n this.parameters = {\n radiusTop: radiusTop,\n radiusBottom: radiusBottom,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n var scope = this;\n\n radiusTop = radiusTop !== undefined ? radiusTop : 1;\n radiusBottom = radiusBottom !== undefined ? radiusBottom : 1;\n height = height || 1;\n\n radialSegments = Math.floor( radialSegments ) || 8;\n heightSegments = Math.floor( heightSegments ) || 1;\n\n openEnded = openEnded !== undefined ? openEnded : false;\n thetaStart = thetaStart !== undefined ? thetaStart : 0.0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var index = 0;\n var indexArray = [];\n var halfHeight = height / 2;\n var groupStart = 0;\n\n // generate geometry\n\n generateTorso();\n\n if ( openEnded === false ) {\n\n if ( radiusTop > 0 ) generateCap( true );\n if ( radiusBottom > 0 ) generateCap( false );\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n function generateTorso() {\n\n var x, y;\n var normal = new Vector3();\n var vertex = new Vector3();\n\n var groupCount = 0;\n\n // this will be used to calculate the normal\n var slope = ( radiusBottom - radiusTop ) / height;\n\n // generate vertices, normals and uvs\n\n for ( y = 0; y <= heightSegments; y ++ ) {\n\n var indexRow = [];\n\n var v = y / heightSegments;\n\n // calculate the radius of the current row\n\n var radius = v * ( radiusBottom - radiusTop ) + radiusTop;\n\n for ( x = 0; x <= radialSegments; x ++ ) {\n\n var u = x / radialSegments;\n\n var theta = u * thetaLength + thetaStart;\n\n var sinTheta = Math.sin( theta );\n var cosTheta = Math.cos( theta );\n\n // vertex\n\n vertex.x = radius * sinTheta;\n vertex.y = - v * height + halfHeight;\n vertex.z = radius * cosTheta;\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normal.set( sinTheta, slope, cosTheta ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, 1 - v );\n\n // save index of vertex in respective row\n\n indexRow.push( index ++ );\n\n }\n\n // now save vertices of the row in our index array\n\n indexArray.push( indexRow );\n\n }\n\n // generate indices\n\n for ( x = 0; x < radialSegments; x ++ ) {\n\n for ( y = 0; y < heightSegments; y ++ ) {\n\n // we use the index array to access the correct indices\n\n var a = indexArray[ y ][ x ];\n var b = indexArray[ y + 1 ][ x ];\n var c = indexArray[ y + 1 ][ x + 1 ];\n var d = indexArray[ y ][ x + 1 ];\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n // update group counter\n\n groupCount += 6;\n\n }\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, 0 );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n }\n\n function generateCap( top ) {\n\n var x, centerIndexStart, centerIndexEnd;\n\n var uv = new Vector2();\n var vertex = new Vector3();\n\n var groupCount = 0;\n\n var radius = ( top === true ) ? radiusTop : radiusBottom;\n var sign = ( top === true ) ? 1 : - 1;\n\n // save the index of the first center vertex\n centerIndexStart = index;\n\n // first we generate the center vertex data of the cap.\n // because the geometry needs one set of uvs per face,\n // we must generate a center vertex per face/segment\n\n for ( x = 1; x <= radialSegments; x ++ ) {\n\n // vertex\n\n vertices.push( 0, halfHeight * sign, 0 );\n\n // normal\n\n normals.push( 0, sign, 0 );\n\n // uv\n\n uvs.push( 0.5, 0.5 );\n\n // increase index\n\n index ++;\n\n }\n\n // save the index of the last center vertex\n\n centerIndexEnd = index;\n\n // now we generate the surrounding vertices, normals and uvs\n\n for ( x = 0; x <= radialSegments; x ++ ) {\n\n var u = x / radialSegments;\n var theta = u * thetaLength + thetaStart;\n\n var cosTheta = Math.cos( theta );\n var sinTheta = Math.sin( theta );\n\n // vertex\n\n vertex.x = radius * sinTheta;\n vertex.y = halfHeight * sign;\n vertex.z = radius * cosTheta;\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, sign, 0 );\n\n // uv\n\n uv.x = ( cosTheta * 0.5 ) + 0.5;\n uv.y = ( sinTheta * 0.5 * sign ) + 0.5;\n uvs.push( uv.x, uv.y );\n\n // increase index\n\n index ++;\n\n }\n\n // generate indices\n\n for ( x = 0; x < radialSegments; x ++ ) {\n\n var c = centerIndexStart + x;\n var i = centerIndexEnd + x;\n\n if ( top === true ) {\n\n // face top\n\n indices.push( i, i + 1, c );\n\n } else {\n\n // face bottom\n\n indices.push( i + 1, i, c );\n\n }\n\n groupCount += 3;\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n }\n\n }\n\n CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry;\n\n /**\n * @author abelnation / http://github.com/abelnation\n */\n\n // ConeGeometry\n\n function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n this.type = 'ConeGeometry';\n\n this.parameters = {\n radius: radius,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n }\n\n ConeGeometry.prototype = Object.create( CylinderGeometry.prototype );\n ConeGeometry.prototype.constructor = ConeGeometry;\n\n // ConeBufferGeometry\n\n function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n this.type = 'ConeBufferGeometry';\n\n this.parameters = {\n radius: radius,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n }\n\n ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype );\n ConeBufferGeometry.prototype.constructor = ConeBufferGeometry;\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n * @author Mugen87 / https://github.com/Mugen87\n * @author hughes\n */\n\n // CircleGeometry\n\n function CircleGeometry( radius, segments, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'CircleGeometry';\n\n this.parameters = {\n radius: radius,\n segments: segments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n CircleGeometry.prototype = Object.create( Geometry.prototype );\n CircleGeometry.prototype.constructor = CircleGeometry;\n\n // CircleBufferGeometry\n\n function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'CircleBufferGeometry';\n\n this.parameters = {\n radius: radius,\n segments: segments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n radius = radius || 1;\n segments = segments !== undefined ? Math.max( 3, segments ) : 8;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var i, s;\n var vertex = new Vector3();\n var uv = new Vector2();\n\n // center point\n\n vertices.push( 0, 0, 0 );\n normals.push( 0, 0, 1 );\n uvs.push( 0.5, 0.5 );\n\n for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) {\n\n var segment = thetaStart + s / segments * thetaLength;\n\n // vertex\n\n vertex.x = radius * Math.cos( segment );\n vertex.y = radius * Math.sin( segment );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, 0, 1 );\n\n // uvs\n\n uv.x = ( vertices[ i ] / radius + 1 ) / 2;\n uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n // indices\n\n for ( i = 1; i <= segments; i ++ ) {\n\n indices.push( i, i + 1, 0 );\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n CircleBufferGeometry.prototype.constructor = CircleBufferGeometry;\n\n\n\n var Geometries = Object.freeze({\n WireframeGeometry: WireframeGeometry,\n ParametricGeometry: ParametricGeometry,\n ParametricBufferGeometry: ParametricBufferGeometry,\n TetrahedronGeometry: TetrahedronGeometry,\n TetrahedronBufferGeometry: TetrahedronBufferGeometry,\n OctahedronGeometry: OctahedronGeometry,\n OctahedronBufferGeometry: OctahedronBufferGeometry,\n IcosahedronGeometry: IcosahedronGeometry,\n IcosahedronBufferGeometry: IcosahedronBufferGeometry,\n DodecahedronGeometry: DodecahedronGeometry,\n DodecahedronBufferGeometry: DodecahedronBufferGeometry,\n PolyhedronGeometry: PolyhedronGeometry,\n PolyhedronBufferGeometry: PolyhedronBufferGeometry,\n TubeGeometry: TubeGeometry,\n TubeBufferGeometry: TubeBufferGeometry,\n TorusKnotGeometry: TorusKnotGeometry,\n TorusKnotBufferGeometry: TorusKnotBufferGeometry,\n TorusGeometry: TorusGeometry,\n TorusBufferGeometry: TorusBufferGeometry,\n TextGeometry: TextGeometry,\n TextBufferGeometry: TextBufferGeometry,\n SphereGeometry: SphereGeometry,\n SphereBufferGeometry: SphereBufferGeometry,\n RingGeometry: RingGeometry,\n RingBufferGeometry: RingBufferGeometry,\n PlaneGeometry: PlaneGeometry,\n PlaneBufferGeometry: PlaneBufferGeometry,\n LatheGeometry: LatheGeometry,\n LatheBufferGeometry: LatheBufferGeometry,\n ShapeGeometry: ShapeGeometry,\n ShapeBufferGeometry: ShapeBufferGeometry,\n ExtrudeGeometry: ExtrudeGeometry,\n ExtrudeBufferGeometry: ExtrudeBufferGeometry,\n EdgesGeometry: EdgesGeometry,\n ConeGeometry: ConeGeometry,\n ConeBufferGeometry: ConeBufferGeometry,\n CylinderGeometry: CylinderGeometry,\n CylinderBufferGeometry: CylinderBufferGeometry,\n CircleGeometry: CircleGeometry,\n CircleBufferGeometry: CircleBufferGeometry,\n BoxGeometry: BoxGeometry,\n BoxBufferGeometry: BoxBufferGeometry\n });\n\n /**\n * @author mrdoob / http://mrdoob.com/\n *\n * parameters = {\n * color: <THREE.Color>\n * }\n */\n\n function ShadowMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'ShadowMaterial';\n\n this.color = new Color( 0x000000 );\n this.transparent = true;\n\n this.setValues( parameters );\n\n }\n\n ShadowMaterial.prototype = Object.create( Material.prototype );\n ShadowMaterial.prototype.constructor = ShadowMaterial;\n\n ShadowMaterial.prototype.isShadowMaterial = true;\n\n ShadowMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function RawShaderMaterial( parameters ) {\n\n ShaderMaterial.call( this, parameters );\n\n this.type = 'RawShaderMaterial';\n\n }\n\n RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype );\n RawShaderMaterial.prototype.constructor = RawShaderMaterial;\n\n RawShaderMaterial.prototype.isRawShaderMaterial = true;\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * color: <hex>,\n * roughness: <float>,\n * metalness: <float>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * roughnessMap: new THREE.Texture( <Image> ),\n *\n * metalnessMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * envMapIntensity: <float>\n *\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshStandardMaterial( parameters ) {\n\n Material.call( this );\n\n this.defines = { 'STANDARD': '' };\n\n this.type = 'MeshStandardMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n this.roughness = 0.5;\n this.metalness = 0.5;\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.roughnessMap = null;\n\n this.metalnessMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.envMapIntensity = 1.0;\n\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshStandardMaterial.prototype = Object.create( Material.prototype );\n MeshStandardMaterial.prototype.constructor = MeshStandardMaterial;\n\n MeshStandardMaterial.prototype.isMeshStandardMaterial = true;\n\n MeshStandardMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.defines = { 'STANDARD': '' };\n\n this.color.copy( source.color );\n this.roughness = source.roughness;\n this.metalness = source.metalness;\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.roughnessMap = source.roughnessMap;\n\n this.metalnessMap = source.metalnessMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.envMapIntensity = source.envMapIntensity;\n\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * reflectivity: <float>\n * }\n */\n\n function MeshPhysicalMaterial( parameters ) {\n\n MeshStandardMaterial.call( this );\n\n this.defines = { 'PHYSICAL': '' };\n\n this.type = 'MeshPhysicalMaterial';\n\n this.reflectivity = 0.5; // maps to F0 = 0.04\n\n this.clearCoat = 0.0;\n this.clearCoatRoughness = 0.0;\n\n this.setValues( parameters );\n\n }\n\n MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype );\n MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial;\n\n MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true;\n\n MeshPhysicalMaterial.prototype.copy = function ( source ) {\n\n MeshStandardMaterial.prototype.copy.call( this, source );\n\n this.defines = { 'PHYSICAL': '' };\n\n this.reflectivity = source.reflectivity;\n\n this.clearCoat = source.clearCoat;\n this.clearCoatRoughness = source.clearCoatRoughness;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * specular: <hex>,\n * shininess: <float>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshPhongMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshPhongMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n this.specular = new Color( 0x111111 );\n this.shininess = 30;\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshPhongMaterial.prototype = Object.create( Material.prototype );\n MeshPhongMaterial.prototype.constructor = MeshPhongMaterial;\n\n MeshPhongMaterial.prototype.isMeshPhongMaterial = true;\n\n MeshPhongMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.specular.copy( source.specular );\n this.shininess = source.shininess;\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author takahirox / http://github.com/takahirox\n *\n * parameters = {\n * gradientMap: new THREE.Texture( <Image> )\n * }\n */\n\n function MeshToonMaterial( parameters ) {\n\n MeshPhongMaterial.call( this );\n\n this.defines = { 'TOON': '' };\n\n this.type = 'MeshToonMaterial';\n\n this.gradientMap = null;\n\n this.setValues( parameters );\n\n }\n\n MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype );\n MeshToonMaterial.prototype.constructor = MeshToonMaterial;\n\n MeshToonMaterial.prototype.isMeshToonMaterial = true;\n\n MeshToonMaterial.prototype.copy = function ( source ) {\n\n MeshPhongMaterial.prototype.copy.call( this, source );\n\n this.gradientMap = source.gradientMap;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * opacity: <float>,\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshNormalMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshNormalMaterial';\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false;\n this.lights = false;\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshNormalMaterial.prototype = Object.create( Material.prototype );\n MeshNormalMaterial.prototype.constructor = MeshNormalMaterial;\n\n MeshNormalMaterial.prototype.isMeshNormalMaterial = true;\n\n MeshNormalMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshLambertMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshLambertMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshLambertMaterial.prototype = Object.create( Material.prototype );\n MeshLambertMaterial.prototype.constructor = MeshLambertMaterial;\n\n MeshLambertMaterial.prototype.isMeshLambertMaterial = true;\n\n MeshLambertMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * linewidth: <float>,\n *\n * scale: <float>,\n * dashSize: <float>,\n * gapSize: <float>\n * }\n */\n\n function LineDashedMaterial( parameters ) {\n\n LineBasicMaterial.call( this );\n\n this.type = 'LineDashedMaterial';\n\n this.scale = 1;\n this.dashSize = 3;\n this.gapSize = 1;\n\n this.setValues( parameters );\n\n }\n\n LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype );\n LineDashedMaterial.prototype.constructor = LineDashedMaterial;\n\n LineDashedMaterial.prototype.isLineDashedMaterial = true;\n\n LineDashedMaterial.prototype.copy = function ( source ) {\n\n LineBasicMaterial.prototype.copy.call( this, source );\n\n this.scale = source.scale;\n this.dashSize = source.dashSize;\n this.gapSize = source.gapSize;\n\n return this;\n\n };\n\n\n\n var Materials = Object.freeze({\n ShadowMaterial: ShadowMaterial,\n SpriteMaterial: SpriteMaterial,\n RawShaderMaterial: RawShaderMaterial,\n ShaderMaterial: ShaderMaterial,\n PointsMaterial: PointsMaterial,\n MeshPhysicalMaterial: MeshPhysicalMaterial,\n MeshStandardMaterial: MeshStandardMaterial,\n MeshPhongMaterial: MeshPhongMaterial,\n MeshToonMaterial: MeshToonMaterial,\n MeshNormalMaterial: MeshNormalMaterial,\n MeshLambertMaterial: MeshLambertMaterial,\n MeshDepthMaterial: MeshDepthMaterial,\n MeshDistanceMaterial: MeshDistanceMaterial,\n MeshBasicMaterial: MeshBasicMaterial,\n LineDashedMaterial: LineDashedMaterial,\n LineBasicMaterial: LineBasicMaterial,\n Material: Material\n });\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var Cache = {\n\n enabled: false,\n\n files: {},\n\n add: function ( key, file ) {\n\n if ( this.enabled === false ) return;\n\n // console.log( 'THREE.Cache', 'Adding key:', key );\n\n this.files[ key ] = file;\n\n },\n\n get: function ( key ) {\n\n if ( this.enabled === false ) return;\n\n // console.log( 'THREE.Cache', 'Checking key:', key );\n\n return this.files[ key ];\n\n },\n\n remove: function ( key ) {\n\n delete this.files[ key ];\n\n },\n\n clear: function () {\n\n this.files = {};\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LoadingManager( onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var isLoading = false;\n var itemsLoaded = 0;\n var itemsTotal = 0;\n var urlModifier = undefined;\n\n this.onStart = undefined;\n this.onLoad = onLoad;\n this.onProgress = onProgress;\n this.onError = onError;\n\n this.itemStart = function ( url ) {\n\n itemsTotal ++;\n\n if ( isLoading === false ) {\n\n if ( scope.onStart !== undefined ) {\n\n scope.onStart( url, itemsLoaded, itemsTotal );\n\n }\n\n }\n\n isLoading = true;\n\n };\n\n this.itemEnd = function ( url ) {\n\n itemsLoaded ++;\n\n if ( scope.onProgress !== undefined ) {\n\n scope.onProgress( url, itemsLoaded, itemsTotal );\n\n }\n\n if ( itemsLoaded === itemsTotal ) {\n\n isLoading = false;\n\n if ( scope.onLoad !== undefined ) {\n\n scope.onLoad();\n\n }\n\n }\n\n };\n\n this.itemError = function ( url ) {\n\n if ( scope.onError !== undefined ) {\n\n scope.onError( url );\n\n }\n\n };\n\n this.resolveURL = function ( url ) {\n\n if ( urlModifier ) {\n\n return urlModifier( url );\n\n }\n\n return url;\n\n };\n\n this.setURLModifier = function ( transform ) {\n\n urlModifier = transform;\n return this;\n\n };\n\n }\n\n var DefaultLoadingManager = new LoadingManager();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var loading = {};\n\n function FileLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( FileLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n url = this.manager.resolveURL( url );\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n // Check if request is duplicate\n\n if ( loading[ url ] !== undefined ) {\n\n loading[ url ].push( {\n\n onLoad: onLoad,\n onProgress: onProgress,\n onError: onError\n\n } );\n\n return;\n\n }\n\n // Check for data: URI\n var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;\n var dataUriRegexResult = url.match( dataUriRegex );\n\n // Safari can not handle Data URIs through XMLHttpRequest so process manually\n if ( dataUriRegexResult ) {\n\n var mimeType = dataUriRegexResult[ 1 ];\n var isBase64 = !! dataUriRegexResult[ 2 ];\n var data = dataUriRegexResult[ 3 ];\n\n data = window.decodeURIComponent( data );\n\n if ( isBase64 ) data = window.atob( data );\n\n try {\n\n var response;\n var responseType = ( this.responseType || '' ).toLowerCase();\n\n switch ( responseType ) {\n\n case 'arraybuffer':\n case 'blob':\n\n var view = new Uint8Array( data.length );\n\n for ( var i = 0; i < data.length; i ++ ) {\n\n view[ i ] = data.charCodeAt( i );\n\n }\n\n if ( responseType === 'blob' ) {\n\n response = new Blob( [ view.buffer ], { type: mimeType } );\n\n } else {\n\n response = view.buffer;\n\n }\n\n break;\n\n case 'document':\n\n var parser = new DOMParser();\n response = parser.parseFromString( data, mimeType );\n\n break;\n\n case 'json':\n\n response = JSON.parse( data );\n\n break;\n\n default: // 'text' or other\n\n response = data;\n\n break;\n\n }\n\n // Wait for next browser tick like standard XMLHttpRequest event dispatching does\n window.setTimeout( function () {\n\n if ( onLoad ) onLoad( response );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n } catch ( error ) {\n\n // Wait for next browser tick like standard XMLHttpRequest event dispatching does\n window.setTimeout( function () {\n\n if ( onError ) onError( error );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, 0 );\n\n }\n\n } else {\n\n // Initialise array for duplicate requests\n\n loading[ url ] = [];\n\n loading[ url ].push( {\n\n onLoad: onLoad,\n onProgress: onProgress,\n onError: onError\n\n } );\n\n var request = new XMLHttpRequest();\n\n request.open( 'GET', url, true );\n\n request.addEventListener( 'load', function ( event ) {\n\n var response = this.response;\n\n Cache.add( url, response );\n\n var callbacks = loading[ url ];\n\n delete loading[ url ];\n\n if ( this.status === 200 ) {\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onLoad ) callback.onLoad( response );\n\n }\n\n scope.manager.itemEnd( url );\n\n } else if ( this.status === 0 ) {\n\n // Some browsers return HTTP Status 0 when using non-http protocol\n // e.g. 'file://' or 'data://'. Handle as success.\n\n console.warn( 'THREE.FileLoader: HTTP Status 0 received.' );\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onLoad ) callback.onLoad( response );\n\n }\n\n scope.manager.itemEnd( url );\n\n } else {\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onError ) callback.onError( event );\n\n }\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }\n\n }, false );\n\n request.addEventListener( 'progress', function ( event ) {\n\n var callbacks = loading[ url ];\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onProgress ) callback.onProgress( event );\n\n }\n\n }, false );\n\n request.addEventListener( 'error', function ( event ) {\n\n var callbacks = loading[ url ];\n\n delete loading[ url ];\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onError ) callback.onError( event );\n\n }\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, false );\n\n if ( this.responseType !== undefined ) request.responseType = this.responseType;\n if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;\n\n if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' );\n\n for ( var header in this.requestHeader ) {\n\n request.setRequestHeader( header, this.requestHeader[ header ] );\n\n }\n\n request.send( null );\n\n }\n\n scope.manager.itemStart( url );\n\n return request;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n },\n\n setResponseType: function ( value ) {\n\n this.responseType = value;\n return this;\n\n },\n\n setWithCredentials: function ( value ) {\n\n this.withCredentials = value;\n return this;\n\n },\n\n setMimeType: function ( value ) {\n\n this.mimeType = value;\n return this;\n\n },\n\n setRequestHeader: function ( value ) {\n\n this.requestHeader = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n *\n * Abstract Base class to block based textures loader (dds, pvr, ...)\n */\n\n function CompressedTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n // override in sub classes\n this._parser = null;\n\n }\n\n Object.assign( CompressedTextureLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var images = [];\n\n var texture = new CompressedTexture();\n texture.image = images;\n\n var loader = new FileLoader( this.manager );\n loader.setPath( this.path );\n loader.setResponseType( 'arraybuffer' );\n\n function loadTexture( i ) {\n\n loader.load( url[ i ], function ( buffer ) {\n\n var texDatas = scope._parser( buffer, true );\n\n images[ i ] = {\n width: texDatas.width,\n height: texDatas.height,\n format: texDatas.format,\n mipmaps: texDatas.mipmaps\n };\n\n loaded += 1;\n\n if ( loaded === 6 ) {\n\n if ( texDatas.mipmapCount === 1 )\n texture.minFilter = LinearFilter;\n\n texture.format = texDatas.format;\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }\n\n }, onProgress, onError );\n\n }\n\n if ( Array.isArray( url ) ) {\n\n var loaded = 0;\n\n for ( var i = 0, il = url.length; i < il; ++ i ) {\n\n loadTexture( i );\n\n }\n\n } else {\n\n // compressed cubemap texture stored in a single DDS file\n\n loader.load( url, function ( buffer ) {\n\n var texDatas = scope._parser( buffer, true );\n\n if ( texDatas.isCubemap ) {\n\n var faces = texDatas.mipmaps.length / texDatas.mipmapCount;\n\n for ( var f = 0; f < faces; f ++ ) {\n\n images[ f ] = { mipmaps: [] };\n\n for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {\n\n images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );\n images[ f ].format = texDatas.format;\n images[ f ].width = texDatas.width;\n images[ f ].height = texDatas.height;\n\n }\n\n }\n\n } else {\n\n texture.image.width = texDatas.width;\n texture.image.height = texDatas.height;\n texture.mipmaps = texDatas.mipmaps;\n\n }\n\n if ( texDatas.mipmapCount === 1 ) {\n\n texture.minFilter = LinearFilter;\n\n }\n\n texture.format = texDatas.format;\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }, onProgress, onError );\n\n }\n\n return texture;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author Nikos M. / https://github.com/foo123/\n *\n * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)\n */\n\n function DataTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n // override in sub classes\n this._parser = null;\n\n }\n\n Object.assign( DataTextureLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var texture = new DataTexture();\n\n var loader = new FileLoader( this.manager );\n loader.setResponseType( 'arraybuffer' );\n\n loader.load( url, function ( buffer ) {\n\n var texData = scope._parser( buffer );\n\n if ( ! texData ) return;\n\n if ( undefined !== texData.image ) {\n\n texture.image = texData.image;\n\n } else if ( undefined !== texData.data ) {\n\n texture.image.width = texData.width;\n texture.image.height = texData.height;\n texture.image.data = texData.data;\n\n }\n\n texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping;\n texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping;\n\n texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter;\n texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter;\n\n texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;\n\n if ( undefined !== texData.format ) {\n\n texture.format = texData.format;\n\n }\n if ( undefined !== texData.type ) {\n\n texture.type = texData.type;\n\n }\n\n if ( undefined !== texData.mipmaps ) {\n\n texture.mipmaps = texData.mipmaps;\n\n }\n\n if ( 1 === texData.mipmapCount ) {\n\n texture.minFilter = LinearFilter;\n\n }\n\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture, texData );\n\n }, onProgress, onError );\n\n\n return texture;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ImageLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( ImageLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n url = this.manager.resolveURL( url );\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );\n\n image.addEventListener( 'load', function () {\n\n Cache.add( url, this );\n\n if ( onLoad ) onLoad( this );\n\n scope.manager.itemEnd( url );\n\n }, false );\n\n /*\n image.addEventListener( 'progress', function ( event ) {\n\n if ( onProgress ) onProgress( event );\n\n }, false );\n */\n\n image.addEventListener( 'error', function ( event ) {\n\n if ( onError ) onError( event );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, false );\n\n if ( url.substr( 0, 5 ) !== 'data:' ) {\n\n if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;\n\n }\n\n scope.manager.itemStart( url );\n\n image.src = url;\n\n return image;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CubeTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( CubeTextureLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( urls, onLoad, onProgress, onError ) {\n\n var texture = new CubeTexture();\n\n var loader = new ImageLoader( this.manager );\n loader.setCrossOrigin( this.crossOrigin );\n loader.setPath( this.path );\n\n var loaded = 0;\n\n function loadTexture( i ) {\n\n loader.load( urls[ i ], function ( image ) {\n\n texture.images[ i ] = image;\n\n loaded ++;\n\n if ( loaded === 6 ) {\n\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }\n\n }, undefined, onError );\n\n }\n\n for ( var i = 0; i < urls.length; ++ i ) {\n\n loadTexture( i );\n\n }\n\n return texture;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function TextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( TextureLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var texture = new Texture();\n\n var loader = new ImageLoader( this.manager );\n loader.setCrossOrigin( this.crossOrigin );\n loader.setPath( this.path );\n\n loader.load( url, function ( image ) {\n\n texture.image = image;\n\n // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.\n var isJPEG = url.search( /\\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\\:image\\/jpeg/ ) === 0;\n\n texture.format = isJPEG ? RGBFormat : RGBAFormat;\n texture.needsUpdate = true;\n\n if ( onLoad !== undefined ) {\n\n onLoad( texture );\n\n }\n\n }, onProgress, onError );\n\n return texture;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Extensible curve object\n *\n * Some common of curve methods:\n * .getPoint( t, optionalTarget ), .getTangent( t )\n * .getPointAt( u, optionalTarget ), .getTangentAt( u )\n * .getPoints(), .getSpacedPoints()\n * .getLength()\n * .updateArcLengths()\n *\n * This following curves inherit from THREE.Curve:\n *\n * -- 2D curves --\n * THREE.ArcCurve\n * THREE.CubicBezierCurve\n * THREE.EllipseCurve\n * THREE.LineCurve\n * THREE.QuadraticBezierCurve\n * THREE.SplineCurve\n *\n * -- 3D curves --\n * THREE.CatmullRomCurve3\n * THREE.CubicBezierCurve3\n * THREE.LineCurve3\n * THREE.QuadraticBezierCurve3\n *\n * A series of curves can be represented as a THREE.CurvePath.\n *\n **/\n\n /**************************************************************\n * Abstract Curve base class\n **************************************************************/\n\n function Curve() {\n\n this.type = 'Curve';\n\n this.arcLengthDivisions = 200;\n\n }\n\n Object.assign( Curve.prototype, {\n\n // Virtual base class method to overwrite and implement in subclasses\n // - t [0 .. 1]\n\n getPoint: function ( /* t, optionalTarget */ ) {\n\n console.warn( 'THREE.Curve: .getPoint() not implemented.' );\n return null;\n\n },\n\n // Get point at relative position in curve according to arc length\n // - u [0 .. 1]\n\n getPointAt: function ( u, optionalTarget ) {\n\n var t = this.getUtoTmapping( u );\n return this.getPoint( t, optionalTarget );\n\n },\n\n // Get sequence of points using getPoint( t )\n\n getPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 5;\n\n var points = [];\n\n for ( var d = 0; d <= divisions; d ++ ) {\n\n points.push( this.getPoint( d / divisions ) );\n\n }\n\n return points;\n\n },\n\n // Get sequence of points using getPointAt( u )\n\n getSpacedPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 5;\n\n var points = [];\n\n for ( var d = 0; d <= divisions; d ++ ) {\n\n points.push( this.getPointAt( d / divisions ) );\n\n }\n\n return points;\n\n },\n\n // Get total curve arc length\n\n getLength: function () {\n\n var lengths = this.getLengths();\n return lengths[ lengths.length - 1 ];\n\n },\n\n // Get list of cumulative segment lengths\n\n getLengths: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = this.arcLengthDivisions;\n\n if ( this.cacheArcLengths &&\n ( this.cacheArcLengths.length === divisions + 1 ) &&\n ! this.needsUpdate ) {\n\n return this.cacheArcLengths;\n\n }\n\n this.needsUpdate = false;\n\n var cache = [];\n var current, last = this.getPoint( 0 );\n var p, sum = 0;\n\n cache.push( 0 );\n\n for ( p = 1; p <= divisions; p ++ ) {\n\n current = this.getPoint( p / divisions );\n sum += current.distanceTo( last );\n cache.push( sum );\n last = current;\n\n }\n\n this.cacheArcLengths = cache;\n\n return cache; // { sums: cache, sum: sum }; Sum is in the last element.\n\n },\n\n updateArcLengths: function () {\n\n this.needsUpdate = true;\n this.getLengths();\n\n },\n\n // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant\n\n getUtoTmapping: function ( u, distance ) {\n\n var arcLengths = this.getLengths();\n\n var i = 0, il = arcLengths.length;\n\n var targetArcLength; // The targeted u distance value to get\n\n if ( distance ) {\n\n targetArcLength = distance;\n\n } else {\n\n targetArcLength = u * arcLengths[ il - 1 ];\n\n }\n\n // binary search for the index with largest value smaller than target u distance\n\n var low = 0, high = il - 1, comparison;\n\n while ( low <= high ) {\n\n i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats\n\n comparison = arcLengths[ i ] - targetArcLength;\n\n if ( comparison < 0 ) {\n\n low = i + 1;\n\n } else if ( comparison > 0 ) {\n\n high = i - 1;\n\n } else {\n\n high = i;\n break;\n\n // DONE\n\n }\n\n }\n\n i = high;\n\n if ( arcLengths[ i ] === targetArcLength ) {\n\n return i / ( il - 1 );\n\n }\n\n // we could get finer grain at lengths, or use simple interpolation between two points\n\n var lengthBefore = arcLengths[ i ];\n var lengthAfter = arcLengths[ i + 1 ];\n\n var segmentLength = lengthAfter - lengthBefore;\n\n // determine where we are between the 'before' and 'after' points\n\n var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;\n\n // add that fractional amount to t\n\n var t = ( i + segmentFraction ) / ( il - 1 );\n\n return t;\n\n },\n\n // Returns a unit vector tangent at t\n // In case any sub curve does not implement its tangent derivation,\n // 2 points a small delta apart will be used to find its gradient\n // which seems to give a reasonable approximation\n\n getTangent: function ( t ) {\n\n var delta = 0.0001;\n var t1 = t - delta;\n var t2 = t + delta;\n\n // Capping in case of danger\n\n if ( t1 < 0 ) t1 = 0;\n if ( t2 > 1 ) t2 = 1;\n\n var pt1 = this.getPoint( t1 );\n var pt2 = this.getPoint( t2 );\n\n var vec = pt2.clone().sub( pt1 );\n return vec.normalize();\n\n },\n\n getTangentAt: function ( u ) {\n\n var t = this.getUtoTmapping( u );\n return this.getTangent( t );\n\n },\n\n computeFrenetFrames: function ( segments, closed ) {\n\n // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n\n var normal = new Vector3();\n\n var tangents = [];\n var normals = [];\n var binormals = [];\n\n var vec = new Vector3();\n var mat = new Matrix4();\n\n var i, u, theta;\n\n // compute the tangent vectors for each segment on the curve\n\n for ( i = 0; i <= segments; i ++ ) {\n\n u = i / segments;\n\n tangents[ i ] = this.getTangentAt( u );\n tangents[ i ].normalize();\n\n }\n\n // select an initial normal vector perpendicular to the first tangent vector,\n // and in the direction of the minimum tangent xyz component\n\n normals[ 0 ] = new Vector3();\n binormals[ 0 ] = new Vector3();\n var min = Number.MAX_VALUE;\n var tx = Math.abs( tangents[ 0 ].x );\n var ty = Math.abs( tangents[ 0 ].y );\n var tz = Math.abs( tangents[ 0 ].z );\n\n if ( tx <= min ) {\n\n min = tx;\n normal.set( 1, 0, 0 );\n\n }\n\n if ( ty <= min ) {\n\n min = ty;\n normal.set( 0, 1, 0 );\n\n }\n\n if ( tz <= min ) {\n\n normal.set( 0, 0, 1 );\n\n }\n\n vec.crossVectors( tangents[ 0 ], normal ).normalize();\n\n normals[ 0 ].crossVectors( tangents[ 0 ], vec );\n binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );\n\n\n // compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n for ( i = 1; i <= segments; i ++ ) {\n\n normals[ i ] = normals[ i - 1 ].clone();\n\n binormals[ i ] = binormals[ i - 1 ].clone();\n\n vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );\n\n if ( vec.length() > Number.EPSILON ) {\n\n vec.normalize();\n\n theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors\n\n normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );\n\n }\n\n binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n }\n\n // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n if ( closed === true ) {\n\n theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );\n theta /= segments;\n\n if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {\n\n theta = - theta;\n\n }\n\n for ( i = 1; i <= segments; i ++ ) {\n\n // twist a little...\n normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );\n binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n }\n\n }\n\n return {\n tangents: tangents,\n normals: normals,\n binormals: binormals\n };\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.arcLengthDivisions = source.arcLengthDivisions;\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Curve',\n generator: 'Curve.toJSON'\n }\n };\n\n data.arcLengthDivisions = this.arcLengthDivisions;\n data.type = this.type;\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n this.arcLengthDivisions = json.arcLengthDivisions;\n\n return this;\n\n }\n\n } );\n\n function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n Curve.call( this );\n\n this.type = 'EllipseCurve';\n\n this.aX = aX || 0;\n this.aY = aY || 0;\n\n this.xRadius = xRadius || 1;\n this.yRadius = yRadius || 1;\n\n this.aStartAngle = aStartAngle || 0;\n this.aEndAngle = aEndAngle || 2 * Math.PI;\n\n this.aClockwise = aClockwise || false;\n\n this.aRotation = aRotation || 0;\n\n }\n\n EllipseCurve.prototype = Object.create( Curve.prototype );\n EllipseCurve.prototype.constructor = EllipseCurve;\n\n EllipseCurve.prototype.isEllipseCurve = true;\n\n EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var twoPi = Math.PI * 2;\n var deltaAngle = this.aEndAngle - this.aStartAngle;\n var samePoints = Math.abs( deltaAngle ) < Number.EPSILON;\n\n // ensures that deltaAngle is 0 .. 2 PI\n while ( deltaAngle < 0 ) deltaAngle += twoPi;\n while ( deltaAngle > twoPi ) deltaAngle -= twoPi;\n\n if ( deltaAngle < Number.EPSILON ) {\n\n if ( samePoints ) {\n\n deltaAngle = 0;\n\n } else {\n\n deltaAngle = twoPi;\n\n }\n\n }\n\n if ( this.aClockwise === true && ! samePoints ) {\n\n if ( deltaAngle === twoPi ) {\n\n deltaAngle = - twoPi;\n\n } else {\n\n deltaAngle = deltaAngle - twoPi;\n\n }\n\n }\n\n var angle = this.aStartAngle + t * deltaAngle;\n var x = this.aX + this.xRadius * Math.cos( angle );\n var y = this.aY + this.yRadius * Math.sin( angle );\n\n if ( this.aRotation !== 0 ) {\n\n var cos = Math.cos( this.aRotation );\n var sin = Math.sin( this.aRotation );\n\n var tx = x - this.aX;\n var ty = y - this.aY;\n\n // Rotate the point about the center of the ellipse.\n x = tx * cos - ty * sin + this.aX;\n y = tx * sin + ty * cos + this.aY;\n\n }\n\n return point.set( x, y );\n\n };\n\n EllipseCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.aX = source.aX;\n this.aY = source.aY;\n\n this.xRadius = source.xRadius;\n this.yRadius = source.yRadius;\n\n this.aStartAngle = source.aStartAngle;\n this.aEndAngle = source.aEndAngle;\n\n this.aClockwise = source.aClockwise;\n\n this.aRotation = source.aRotation;\n\n return this;\n\n };\n\n\n EllipseCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.aX = this.aX;\n data.aY = this.aY;\n\n data.xRadius = this.xRadius;\n data.yRadius = this.yRadius;\n\n data.aStartAngle = this.aStartAngle;\n data.aEndAngle = this.aEndAngle;\n\n data.aClockwise = this.aClockwise;\n\n data.aRotation = this.aRotation;\n\n return data;\n\n };\n\n EllipseCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.aX = json.aX;\n this.aY = json.aY;\n\n this.xRadius = json.xRadius;\n this.yRadius = json.yRadius;\n\n this.aStartAngle = json.aStartAngle;\n this.aEndAngle = json.aEndAngle;\n\n this.aClockwise = json.aClockwise;\n\n this.aRotation = json.aRotation;\n\n return this;\n\n };\n\n function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n this.type = 'ArcCurve';\n\n }\n\n ArcCurve.prototype = Object.create( EllipseCurve.prototype );\n ArcCurve.prototype.constructor = ArcCurve;\n\n ArcCurve.prototype.isArcCurve = true;\n\n /**\n * @author zz85 https://github.com/zz85\n *\n * Centripetal CatmullRom Curve - which is useful for avoiding\n * cusps and self-intersections in non-uniform catmull rom curves.\n * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf\n *\n * curve.type accepts centripetal(default), chordal and catmullrom\n * curve.tension is used for catmullrom which defaults to 0.5\n */\n\n\n /*\n Based on an optimized c++ solution in\n - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/\n - http://ideone.com/NoEbVM\n\n This CubicPoly class could be used for reusing some variables and calculations,\n but for three.js curve use, it could be possible inlined and flatten into a single function call\n which can be placed in CurveUtils.\n */\n\n function CubicPoly() {\n\n var c0 = 0, c1 = 0, c2 = 0, c3 = 0;\n\n /*\n * Compute coefficients for a cubic polynomial\n * p(s) = c0 + c1*s + c2*s^2 + c3*s^3\n * such that\n * p(0) = x0, p(1) = x1\n * and\n * p'(0) = t0, p'(1) = t1.\n */\n function init( x0, x1, t0, t1 ) {\n\n c0 = x0;\n c1 = t0;\n c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;\n c3 = 2 * x0 - 2 * x1 + t0 + t1;\n\n }\n\n return {\n\n initCatmullRom: function ( x0, x1, x2, x3, tension ) {\n\n init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );\n\n },\n\n initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {\n\n // compute tangents when parameterized in [t1,t2]\n var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;\n var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;\n\n // rescale tangents for parametrization in [0,1]\n t1 *= dt1;\n t2 *= dt1;\n\n init( x1, x2, t1, t2 );\n\n },\n\n calc: function ( t ) {\n\n var t2 = t * t;\n var t3 = t2 * t;\n return c0 + c1 * t + c2 * t2 + c3 * t3;\n\n }\n\n };\n\n }\n\n //\n\n var tmp = new Vector3();\n var px = new CubicPoly();\n var py = new CubicPoly();\n var pz = new CubicPoly();\n\n function CatmullRomCurve3( points, closed, curveType, tension ) {\n\n Curve.call( this );\n\n this.type = 'CatmullRomCurve3';\n\n this.points = points || [];\n this.closed = closed || false;\n this.curveType = curveType || 'centripetal';\n this.tension = tension || 0.5;\n\n }\n\n CatmullRomCurve3.prototype = Object.create( Curve.prototype );\n CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;\n\n CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;\n\n CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var points = this.points;\n var l = points.length;\n\n var p = ( l - ( this.closed ? 0 : 1 ) ) * t;\n var intPoint = Math.floor( p );\n var weight = p - intPoint;\n\n if ( this.closed ) {\n\n intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;\n\n } else if ( weight === 0 && intPoint === l - 1 ) {\n\n intPoint = l - 2;\n weight = 1;\n\n }\n\n var p0, p1, p2, p3; // 4 points\n\n if ( this.closed || intPoint > 0 ) {\n\n p0 = points[ ( intPoint - 1 ) % l ];\n\n } else {\n\n // extrapolate first point\n tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );\n p0 = tmp;\n\n }\n\n p1 = points[ intPoint % l ];\n p2 = points[ ( intPoint + 1 ) % l ];\n\n if ( this.closed || intPoint + 2 < l ) {\n\n p3 = points[ ( intPoint + 2 ) % l ];\n\n } else {\n\n // extrapolate last point\n tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );\n p3 = tmp;\n\n }\n\n if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {\n\n // init Centripetal / Chordal Catmull-Rom\n var pow = this.curveType === 'chordal' ? 0.5 : 0.25;\n var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );\n var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );\n var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );\n\n // safety check for repeated points\n if ( dt1 < 1e-4 ) dt1 = 1.0;\n if ( dt0 < 1e-4 ) dt0 = dt1;\n if ( dt2 < 1e-4 ) dt2 = dt1;\n\n px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );\n py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );\n pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );\n\n } else if ( this.curveType === 'catmullrom' ) {\n\n px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );\n py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );\n pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );\n\n }\n\n point.set(\n px.calc( weight ),\n py.calc( weight ),\n pz.calc( weight )\n );\n\n return point;\n\n };\n\n CatmullRomCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.points = [];\n\n for ( var i = 0, l = source.points.length; i < l; i ++ ) {\n\n var point = source.points[ i ];\n\n this.points.push( point.clone() );\n\n }\n\n this.closed = source.closed;\n this.curveType = source.curveType;\n this.tension = source.tension;\n\n return this;\n\n };\n\n CatmullRomCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.points = [];\n\n for ( var i = 0, l = this.points.length; i < l; i ++ ) {\n\n var point = this.points[ i ];\n data.points.push( point.toArray() );\n\n }\n\n data.closed = this.closed;\n data.curveType = this.curveType;\n data.tension = this.tension;\n\n return data;\n\n };\n\n CatmullRomCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.points = [];\n\n for ( var i = 0, l = json.points.length; i < l; i ++ ) {\n\n var point = json.points[ i ];\n this.points.push( new Vector3().fromArray( point ) );\n\n }\n\n this.closed = json.closed;\n this.curveType = json.curveType;\n this.tension = json.tension;\n\n return this;\n\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n * Bezier Curves formulas obtained from\n * http://en.wikipedia.org/wiki/Bézier_curve\n */\n\n function CatmullRom( t, p0, p1, p2, p3 ) {\n\n var v0 = ( p2 - p0 ) * 0.5;\n var v1 = ( p3 - p1 ) * 0.5;\n var t2 = t * t;\n var t3 = t * t2;\n return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;\n\n }\n\n //\n\n function QuadraticBezierP0( t, p ) {\n\n var k = 1 - t;\n return k * k * p;\n\n }\n\n function QuadraticBezierP1( t, p ) {\n\n return 2 * ( 1 - t ) * t * p;\n\n }\n\n function QuadraticBezierP2( t, p ) {\n\n return t * t * p;\n\n }\n\n function QuadraticBezier( t, p0, p1, p2 ) {\n\n return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +\n QuadraticBezierP2( t, p2 );\n\n }\n\n //\n\n function CubicBezierP0( t, p ) {\n\n var k = 1 - t;\n return k * k * k * p;\n\n }\n\n function CubicBezierP1( t, p ) {\n\n var k = 1 - t;\n return 3 * k * k * t * p;\n\n }\n\n function CubicBezierP2( t, p ) {\n\n return 3 * ( 1 - t ) * t * t * p;\n\n }\n\n function CubicBezierP3( t, p ) {\n\n return t * t * t * p;\n\n }\n\n function CubicBezier( t, p0, p1, p2, p3 ) {\n\n return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +\n CubicBezierP3( t, p3 );\n\n }\n\n function CubicBezierCurve( v0, v1, v2, v3 ) {\n\n Curve.call( this );\n\n this.type = 'CubicBezierCurve';\n\n this.v0 = v0 || new Vector2();\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n this.v3 = v3 || new Vector2();\n\n }\n\n CubicBezierCurve.prototype = Object.create( Curve.prototype );\n CubicBezierCurve.prototype.constructor = CubicBezierCurve;\n\n CubicBezierCurve.prototype.isCubicBezierCurve = true;\n\n CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n point.set(\n CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n CubicBezier( t, v0.y, v1.y, v2.y, v3.y )\n );\n\n return point;\n\n };\n\n CubicBezierCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n this.v3.copy( source.v3 );\n\n return this;\n\n };\n\n CubicBezierCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n data.v3 = this.v3.toArray();\n\n return data;\n\n };\n\n CubicBezierCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n this.v3.fromArray( json.v3 );\n\n return this;\n\n };\n\n function CubicBezierCurve3( v0, v1, v2, v3 ) {\n\n Curve.call( this );\n\n this.type = 'CubicBezierCurve3';\n\n this.v0 = v0 || new Vector3();\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n this.v3 = v3 || new Vector3();\n\n }\n\n CubicBezierCurve3.prototype = Object.create( Curve.prototype );\n CubicBezierCurve3.prototype.constructor = CubicBezierCurve3;\n\n CubicBezierCurve3.prototype.isCubicBezierCurve3 = true;\n\n CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n point.set(\n CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n CubicBezier( t, v0.y, v1.y, v2.y, v3.y ),\n CubicBezier( t, v0.z, v1.z, v2.z, v3.z )\n );\n\n return point;\n\n };\n\n CubicBezierCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n this.v3.copy( source.v3 );\n\n return this;\n\n };\n\n CubicBezierCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n data.v3 = this.v3.toArray();\n\n return data;\n\n };\n\n CubicBezierCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n this.v3.fromArray( json.v3 );\n\n return this;\n\n };\n\n function LineCurve( v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'LineCurve';\n\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n\n }\n\n LineCurve.prototype = Object.create( Curve.prototype );\n LineCurve.prototype.constructor = LineCurve;\n\n LineCurve.prototype.isLineCurve = true;\n\n LineCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n if ( t === 1 ) {\n\n point.copy( this.v2 );\n\n } else {\n\n point.copy( this.v2 ).sub( this.v1 );\n point.multiplyScalar( t ).add( this.v1 );\n\n }\n\n return point;\n\n };\n\n // Line curve is linear, so we can overwrite default getPointAt\n\n LineCurve.prototype.getPointAt = function ( u, optionalTarget ) {\n\n return this.getPoint( u, optionalTarget );\n\n };\n\n LineCurve.prototype.getTangent = function ( /* t */ ) {\n\n var tangent = this.v2.clone().sub( this.v1 );\n\n return tangent.normalize();\n\n };\n\n LineCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n LineCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n LineCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function LineCurve3( v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'LineCurve3';\n\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n\n }\n\n LineCurve3.prototype = Object.create( Curve.prototype );\n LineCurve3.prototype.constructor = LineCurve3;\n\n LineCurve3.prototype.isLineCurve3 = true;\n\n LineCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n if ( t === 1 ) {\n\n point.copy( this.v2 );\n\n } else {\n\n point.copy( this.v2 ).sub( this.v1 );\n point.multiplyScalar( t ).add( this.v1 );\n\n }\n\n return point;\n\n };\n\n // Line curve is linear, so we can overwrite default getPointAt\n\n LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) {\n\n return this.getPoint( u, optionalTarget );\n\n };\n\n LineCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n LineCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n LineCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function QuadraticBezierCurve( v0, v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'QuadraticBezierCurve';\n\n this.v0 = v0 || new Vector2();\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n\n }\n\n QuadraticBezierCurve.prototype = Object.create( Curve.prototype );\n QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve;\n\n QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true;\n\n QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n point.set(\n QuadraticBezier( t, v0.x, v1.x, v2.x ),\n QuadraticBezier( t, v0.y, v1.y, v2.y )\n );\n\n return point;\n\n };\n\n QuadraticBezierCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n QuadraticBezierCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n QuadraticBezierCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function QuadraticBezierCurve3( v0, v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'QuadraticBezierCurve3';\n\n this.v0 = v0 || new Vector3();\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n\n }\n\n QuadraticBezierCurve3.prototype = Object.create( Curve.prototype );\n QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3;\n\n QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true;\n\n QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n point.set(\n QuadraticBezier( t, v0.x, v1.x, v2.x ),\n QuadraticBezier( t, v0.y, v1.y, v2.y ),\n QuadraticBezier( t, v0.z, v1.z, v2.z )\n );\n\n return point;\n\n };\n\n QuadraticBezierCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n QuadraticBezierCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n QuadraticBezierCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function SplineCurve( points /* array of Vector2 */ ) {\n\n Curve.call( this );\n\n this.type = 'SplineCurve';\n\n this.points = points || [];\n\n }\n\n SplineCurve.prototype = Object.create( Curve.prototype );\n SplineCurve.prototype.constructor = SplineCurve;\n\n SplineCurve.prototype.isSplineCurve = true;\n\n SplineCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var points = this.points;\n var p = ( points.length - 1 ) * t;\n\n var intPoint = Math.floor( p );\n var weight = p - intPoint;\n\n var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];\n var p1 = points[ intPoint ];\n var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];\n var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];\n\n point.set(\n CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),\n CatmullRom( weight, p0.y, p1.y, p2.y, p3.y )\n );\n\n return point;\n\n };\n\n SplineCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.points = [];\n\n for ( var i = 0, l = source.points.length; i < l; i ++ ) {\n\n var point = source.points[ i ];\n\n this.points.push( point.clone() );\n\n }\n\n return this;\n\n };\n\n SplineCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.points = [];\n\n for ( var i = 0, l = this.points.length; i < l; i ++ ) {\n\n var point = this.points[ i ];\n data.points.push( point.toArray() );\n\n }\n\n return data;\n\n };\n\n SplineCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.points = [];\n\n for ( var i = 0, l = json.points.length; i < l; i ++ ) {\n\n var point = json.points[ i ];\n this.points.push( new Vector2().fromArray( point ) );\n\n }\n\n return this;\n\n };\n\n\n\n var Curves = Object.freeze({\n ArcCurve: ArcCurve,\n CatmullRomCurve3: CatmullRomCurve3,\n CubicBezierCurve: CubicBezierCurve,\n CubicBezierCurve3: CubicBezierCurve3,\n EllipseCurve: EllipseCurve,\n LineCurve: LineCurve,\n LineCurve3: LineCurve3,\n QuadraticBezierCurve: QuadraticBezierCurve,\n QuadraticBezierCurve3: QuadraticBezierCurve3,\n SplineCurve: SplineCurve\n });\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n **/\n\n /**************************************************************\n * Curved Path - a curve path is simply a array of connected\n * curves, but retains the api of a curve\n **************************************************************/\n\n function CurvePath() {\n\n Curve.call( this );\n\n this.type = 'CurvePath';\n\n this.curves = [];\n this.autoClose = false; // Automatically closes the path\n\n }\n\n CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {\n\n constructor: CurvePath,\n\n add: function ( curve ) {\n\n this.curves.push( curve );\n\n },\n\n closePath: function () {\n\n // Add a line curve if start and end of lines are not connected\n var startPoint = this.curves[ 0 ].getPoint( 0 );\n var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );\n\n if ( ! startPoint.equals( endPoint ) ) {\n\n this.curves.push( new LineCurve( endPoint, startPoint ) );\n\n }\n\n },\n\n // To get accurate point with reference to\n // entire path distance at time t,\n // following has to be done:\n\n // 1. Length of each sub path have to be known\n // 2. Locate and identify type of curve\n // 3. Get t for the curve\n // 4. Return curve.getPointAt(t')\n\n getPoint: function ( t ) {\n\n var d = t * this.getLength();\n var curveLengths = this.getCurveLengths();\n var i = 0;\n\n // To think about boundaries points.\n\n while ( i < curveLengths.length ) {\n\n if ( curveLengths[ i ] >= d ) {\n\n var diff = curveLengths[ i ] - d;\n var curve = this.curves[ i ];\n\n var segmentLength = curve.getLength();\n var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;\n\n return curve.getPointAt( u );\n\n }\n\n i ++;\n\n }\n\n return null;\n\n // loop where sum != 0, sum > d , sum+1 <d\n\n },\n\n // We cannot use the default THREE.Curve getPoint() with getLength() because in\n // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath\n // getPoint() depends on getLength\n\n getLength: function () {\n\n var lens = this.getCurveLengths();\n return lens[ lens.length - 1 ];\n\n },\n\n // cacheLengths must be recalculated.\n updateArcLengths: function () {\n\n this.needsUpdate = true;\n this.cacheLengths = null;\n this.getCurveLengths();\n\n },\n\n // Compute lengths and cache them\n // We cannot overwrite getLengths() because UtoT mapping uses it.\n\n getCurveLengths: function () {\n\n // We use cache values if curves and cache array are same length\n\n if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {\n\n return this.cacheLengths;\n\n }\n\n // Get length of sub-curve\n // Push sums into cached array\n\n var lengths = [], sums = 0;\n\n for ( var i = 0, l = this.curves.length; i < l; i ++ ) {\n\n sums += this.curves[ i ].getLength();\n lengths.push( sums );\n\n }\n\n this.cacheLengths = lengths;\n\n return lengths;\n\n },\n\n getSpacedPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 40;\n\n var points = [];\n\n for ( var i = 0; i <= divisions; i ++ ) {\n\n points.push( this.getPoint( i / divisions ) );\n\n }\n\n if ( this.autoClose ) {\n\n points.push( points[ 0 ] );\n\n }\n\n return points;\n\n },\n\n getPoints: function ( divisions ) {\n\n divisions = divisions || 12;\n\n var points = [], last;\n\n for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {\n\n var curve = curves[ i ];\n var resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2\n : ( curve && curve.isLineCurve ) ? 1\n : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length\n : divisions;\n\n var pts = curve.getPoints( resolution );\n\n for ( var j = 0; j < pts.length; j ++ ) {\n\n var point = pts[ j ];\n\n if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates\n\n points.push( point );\n last = point;\n\n }\n\n }\n\n if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {\n\n points.push( points[ 0 ] );\n\n }\n\n return points;\n\n },\n\n copy: function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.curves = [];\n\n for ( var i = 0, l = source.curves.length; i < l; i ++ ) {\n\n var curve = source.curves[ i ];\n\n this.curves.push( curve.clone() );\n\n }\n\n this.autoClose = source.autoClose;\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.autoClose = this.autoClose;\n data.curves = [];\n\n for ( var i = 0, l = this.curves.length; i < l; i ++ ) {\n\n var curve = this.curves[ i ];\n data.curves.push( curve.toJSON() );\n\n }\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.autoClose = json.autoClose;\n this.curves = [];\n\n for ( var i = 0, l = json.curves.length; i < l; i ++ ) {\n\n var curve = json.curves[ i ];\n this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Creates free form 2d path using series of points, lines or curves.\n **/\n\n function Path( points ) {\n\n CurvePath.call( this );\n\n this.type = 'Path';\n\n this.currentPoint = new Vector2();\n\n if ( points ) {\n\n this.setFromPoints( points );\n\n }\n\n }\n\n Path.prototype = Object.assign( Object.create( CurvePath.prototype ), {\n\n constructor: Path,\n\n setFromPoints: function ( points ) {\n\n this.moveTo( points[ 0 ].x, points[ 0 ].y );\n\n for ( var i = 1, l = points.length; i < l; i ++ ) {\n\n this.lineTo( points[ i ].x, points[ i ].y );\n\n }\n\n },\n\n moveTo: function ( x, y ) {\n\n this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?\n\n },\n\n lineTo: function ( x, y ) {\n\n var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );\n this.curves.push( curve );\n\n this.currentPoint.set( x, y );\n\n },\n\n quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {\n\n var curve = new QuadraticBezierCurve(\n this.currentPoint.clone(),\n new Vector2( aCPx, aCPy ),\n new Vector2( aX, aY )\n );\n\n this.curves.push( curve );\n\n this.currentPoint.set( aX, aY );\n\n },\n\n bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n var curve = new CubicBezierCurve(\n this.currentPoint.clone(),\n new Vector2( aCP1x, aCP1y ),\n new Vector2( aCP2x, aCP2y ),\n new Vector2( aX, aY )\n );\n\n this.curves.push( curve );\n\n this.currentPoint.set( aX, aY );\n\n },\n\n splineThru: function ( pts /*Array of Vector*/ ) {\n\n var npts = [ this.currentPoint.clone() ].concat( pts );\n\n var curve = new SplineCurve( npts );\n this.curves.push( curve );\n\n this.currentPoint.copy( pts[ pts.length - 1 ] );\n\n },\n\n arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n var x0 = this.currentPoint.x;\n var y0 = this.currentPoint.y;\n\n this.absarc( aX + x0, aY + y0, aRadius,\n aStartAngle, aEndAngle, aClockwise );\n\n },\n\n absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n },\n\n ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n var x0 = this.currentPoint.x;\n var y0 = this.currentPoint.y;\n\n this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n },\n\n absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n if ( this.curves.length > 0 ) {\n\n // if a previous curve is present, attempt to join\n var firstPoint = curve.getPoint( 0 );\n\n if ( ! firstPoint.equals( this.currentPoint ) ) {\n\n this.lineTo( firstPoint.x, firstPoint.y );\n\n }\n\n }\n\n this.curves.push( curve );\n\n var lastPoint = curve.getPoint( 1 );\n this.currentPoint.copy( lastPoint );\n\n },\n\n copy: function ( source ) {\n\n CurvePath.prototype.copy.call( this, source );\n\n this.currentPoint.copy( source.currentPoint );\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = CurvePath.prototype.toJSON.call( this );\n\n data.currentPoint = this.currentPoint.toArray();\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n CurvePath.prototype.fromJSON.call( this, json );\n\n this.currentPoint.fromArray( json.currentPoint );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Defines a 2d shape plane using paths.\n **/\n\n // STEP 1 Create a path.\n // STEP 2 Turn path into shape.\n // STEP 3 ExtrudeGeometry takes in Shape/Shapes\n // STEP 3a - Extract points from each shape, turn to vertices\n // STEP 3b - Triangulate each shape, add faces.\n\n function Shape( points ) {\n\n Path.call( this, points );\n\n this.uuid = _Math.generateUUID();\n\n this.type = 'Shape';\n\n this.holes = [];\n\n }\n\n Shape.prototype = Object.assign( Object.create( Path.prototype ), {\n\n constructor: Shape,\n\n getPointsHoles: function ( divisions ) {\n\n var holesPts = [];\n\n for ( var i = 0, l = this.holes.length; i < l; i ++ ) {\n\n holesPts[ i ] = this.holes[ i ].getPoints( divisions );\n\n }\n\n return holesPts;\n\n },\n\n // get points of shape and holes (keypoints based on segments parameter)\n\n extractPoints: function ( divisions ) {\n\n return {\n\n shape: this.getPoints( divisions ),\n holes: this.getPointsHoles( divisions )\n\n };\n\n },\n\n copy: function ( source ) {\n\n Path.prototype.copy.call( this, source );\n\n this.holes = [];\n\n for ( var i = 0, l = source.holes.length; i < l; i ++ ) {\n\n var hole = source.holes[ i ];\n\n this.holes.push( hole.clone() );\n\n }\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = Path.prototype.toJSON.call( this );\n\n data.uuid = this.uuid;\n data.holes = [];\n\n for ( var i = 0, l = this.holes.length; i < l; i ++ ) {\n\n var hole = this.holes[ i ];\n data.holes.push( hole.toJSON() );\n\n }\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n Path.prototype.fromJSON.call( this, json );\n\n this.uuid = json.uuid;\n this.holes = [];\n\n for ( var i = 0, l = json.holes.length; i < l; i ++ ) {\n\n var hole = json.holes[ i ];\n this.holes.push( new Path().fromJSON( hole ) );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Light( color, intensity ) {\n\n Object3D.call( this );\n\n this.type = 'Light';\n\n this.color = new Color( color );\n this.intensity = intensity !== undefined ? intensity : 1;\n\n this.receiveShadow = undefined;\n\n }\n\n Light.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Light,\n\n isLight: true,\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.intensity = source.intensity;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.color = this.color.getHex();\n data.object.intensity = this.intensity;\n\n if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();\n\n if ( this.distance !== undefined ) data.object.distance = this.distance;\n if ( this.angle !== undefined ) data.object.angle = this.angle;\n if ( this.decay !== undefined ) data.object.decay = this.decay;\n if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;\n\n if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();\n\n return data;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function HemisphereLight( skyColor, groundColor, intensity ) {\n\n Light.call( this, skyColor, intensity );\n\n this.type = 'HemisphereLight';\n\n this.castShadow = undefined;\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.groundColor = new Color( groundColor );\n\n }\n\n HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: HemisphereLight,\n\n isHemisphereLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.groundColor.copy( source.groundColor );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LightShadow( camera ) {\n\n this.camera = camera;\n\n this.bias = 0;\n this.radius = 1;\n\n this.mapSize = new Vector2( 512, 512 );\n\n this.map = null;\n this.matrix = new Matrix4();\n\n }\n\n Object.assign( LightShadow.prototype, {\n\n copy: function ( source ) {\n\n this.camera = source.camera.clone();\n\n this.bias = source.bias;\n this.radius = source.radius;\n\n this.mapSize.copy( source.mapSize );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n toJSON: function () {\n\n var object = {};\n\n if ( this.bias !== 0 ) object.bias = this.bias;\n if ( this.radius !== 1 ) object.radius = this.radius;\n if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();\n\n object.camera = this.camera.toJSON( false ).object;\n delete object.camera.matrix;\n\n return object;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function SpotLightShadow() {\n\n LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) );\n\n }\n\n SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n constructor: SpotLightShadow,\n\n isSpotLightShadow: true,\n\n update: function ( light ) {\n\n var camera = this.camera;\n\n var fov = _Math.RAD2DEG * 2 * light.angle;\n var aspect = this.mapSize.width / this.mapSize.height;\n var far = light.distance || camera.far;\n\n if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {\n\n camera.fov = fov;\n camera.aspect = aspect;\n camera.far = far;\n camera.updateProjectionMatrix();\n\n }\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function SpotLight( color, intensity, distance, angle, penumbra, decay ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'SpotLight';\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.target = new Object3D();\n\n Object.defineProperty( this, 'power', {\n get: function () {\n\n // intensity = power per solid angle.\n // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n return this.intensity * Math.PI;\n\n },\n set: function ( power ) {\n\n // intensity = power per solid angle.\n // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n this.intensity = power / Math.PI;\n\n }\n } );\n\n this.distance = ( distance !== undefined ) ? distance : 0;\n this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;\n this.penumbra = ( penumbra !== undefined ) ? penumbra : 0;\n this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.\n\n this.shadow = new SpotLightShadow();\n\n }\n\n SpotLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: SpotLight,\n\n isSpotLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.distance = source.distance;\n this.angle = source.angle;\n this.penumbra = source.penumbra;\n this.decay = source.decay;\n\n this.target = source.target.clone();\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n\n function PointLight( color, intensity, distance, decay ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'PointLight';\n\n Object.defineProperty( this, 'power', {\n get: function () {\n\n // intensity = power per solid angle.\n // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n return this.intensity * 4 * Math.PI;\n\n },\n set: function ( power ) {\n\n // intensity = power per solid angle.\n // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n this.intensity = power / ( 4 * Math.PI );\n\n }\n } );\n\n this.distance = ( distance !== undefined ) ? distance : 0;\n this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.\n\n this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) );\n\n }\n\n PointLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: PointLight,\n\n isPointLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.distance = source.distance;\n this.decay = source.decay;\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function DirectionalLightShadow( ) {\n\n LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );\n\n }\n\n DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n constructor: DirectionalLightShadow\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function DirectionalLight( color, intensity ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'DirectionalLight';\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.target = new Object3D();\n\n this.shadow = new DirectionalLightShadow();\n\n }\n\n DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: DirectionalLight,\n\n isDirectionalLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.target = source.target.clone();\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AmbientLight( color, intensity ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'AmbientLight';\n\n this.castShadow = undefined;\n\n }\n\n AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: AmbientLight,\n\n isAmbientLight: true\n\n } );\n\n /**\n * @author abelnation / http://github.com/abelnation\n */\n\n function RectAreaLight( color, intensity, width, height ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'RectAreaLight';\n\n this.width = ( width !== undefined ) ? width : 10;\n this.height = ( height !== undefined ) ? height : 10;\n\n }\n\n RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: RectAreaLight,\n\n isRectAreaLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.width = source.width;\n this.height = source.height;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Light.prototype.toJSON.call( this, meta );\n\n data.object.width = this.width;\n data.object.height = this.height;\n\n return data;\n\n }\n\n } );\n\n /**\n *\n * A Track that interpolates Strings\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function StringKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: StringKeyframeTrack,\n\n ValueTypeName: 'string',\n ValueBufferType: Array,\n\n DefaultInterpolation: InterpolateDiscrete,\n\n InterpolantFactoryMethodLinear: undefined,\n\n InterpolantFactoryMethodSmooth: undefined\n\n } );\n\n /**\n *\n * A Track of Boolean keyframe values.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function BooleanKeyframeTrack( name, times, values ) {\n\n KeyframeTrack.call( this, name, times, values );\n\n }\n\n BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: BooleanKeyframeTrack,\n\n ValueTypeName: 'bool',\n ValueBufferType: Array,\n\n DefaultInterpolation: InterpolateDiscrete,\n\n InterpolantFactoryMethodLinear: undefined,\n InterpolantFactoryMethodSmooth: undefined\n\n // Note: Actually this track could have a optimized / compressed\n // representation of a single value and a custom interpolant that\n // computes \"firstValue ^ isOdd( index )\".\n\n } );\n\n /**\n * Abstract base class of interpolants over parametric samples.\n *\n * The parameter domain is one dimensional, typically the time or a path\n * along a curve defined by the data.\n *\n * The sample values can have any dimensionality and derived classes may\n * apply special interpretations to the data.\n *\n * This class provides the interval seek in a Template Method, deferring\n * the actual interpolation to derived classes.\n *\n * Time complexity is O(1) for linear access crossing at most two points\n * and O(log N) for random access, where N is the number of positions.\n *\n * References:\n *\n * http://www.oodesign.com/template-method-pattern.html\n *\n * @author tschw\n */\n\n function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n this.parameterPositions = parameterPositions;\n this._cachedIndex = 0;\n\n this.resultBuffer = resultBuffer !== undefined ?\n resultBuffer : new sampleValues.constructor( sampleSize );\n this.sampleValues = sampleValues;\n this.valueSize = sampleSize;\n\n }\n\n Object.assign( Interpolant.prototype, {\n\n evaluate: function ( t ) {\n\n var pp = this.parameterPositions,\n i1 = this._cachedIndex,\n\n t1 = pp[ i1 ],\n t0 = pp[ i1 - 1 ];\n\n validate_interval: {\n\n seek: {\n\n var right;\n\n linear_scan: {\n\n //- See http://jsperf.com/comparison-to-undefined/3\n //- slower code:\n //-\n //- if ( t >= t1 || t1 === undefined ) {\n forward_scan: if ( ! ( t < t1 ) ) {\n\n for ( var giveUpAt = i1 + 2; ; ) {\n\n if ( t1 === undefined ) {\n\n if ( t < t0 ) break forward_scan;\n\n // after end\n\n i1 = pp.length;\n this._cachedIndex = i1;\n return this.afterEnd_( i1 - 1, t, t0 );\n\n }\n\n if ( i1 === giveUpAt ) break; // this loop\n\n t0 = t1;\n t1 = pp[ ++ i1 ];\n\n if ( t < t1 ) {\n\n // we have arrived at the sought interval\n break seek;\n\n }\n\n }\n\n // prepare binary search on the right side of the index\n right = pp.length;\n break linear_scan;\n\n }\n\n //- slower code:\n //- if ( t < t0 || t0 === undefined ) {\n if ( ! ( t >= t0 ) ) {\n\n // looping?\n\n var t1global = pp[ 1 ];\n\n if ( t < t1global ) {\n\n i1 = 2; // + 1, using the scan for the details\n t0 = t1global;\n\n }\n\n // linear reverse scan\n\n for ( var giveUpAt = i1 - 2; ; ) {\n\n if ( t0 === undefined ) {\n\n // before start\n\n this._cachedIndex = 0;\n return this.beforeStart_( 0, t, t1 );\n\n }\n\n if ( i1 === giveUpAt ) break; // this loop\n\n t1 = t0;\n t0 = pp[ -- i1 - 1 ];\n\n if ( t >= t0 ) {\n\n // we have arrived at the sought interval\n break seek;\n\n }\n\n }\n\n // prepare binary search on the left side of the index\n right = i1;\n i1 = 0;\n break linear_scan;\n\n }\n\n // the interval is valid\n\n break validate_interval;\n\n } // linear scan\n\n // binary search\n\n while ( i1 < right ) {\n\n var mid = ( i1 + right ) >>> 1;\n\n if ( t < pp[ mid ] ) {\n\n right = mid;\n\n } else {\n\n i1 = mid + 1;\n\n }\n\n }\n\n t1 = pp[ i1 ];\n t0 = pp[ i1 - 1 ];\n\n // check boundary cases, again\n\n if ( t0 === undefined ) {\n\n this._cachedIndex = 0;\n return this.beforeStart_( 0, t, t1 );\n\n }\n\n if ( t1 === undefined ) {\n\n i1 = pp.length;\n this._cachedIndex = i1;\n return this.afterEnd_( i1 - 1, t0, t );\n\n }\n\n } // seek\n\n this._cachedIndex = i1;\n\n this.intervalChanged_( i1, t0, t1 );\n\n } // validate_interval\n\n return this.interpolate_( i1, t0, t, t1 );\n\n },\n\n settings: null, // optional, subclass-specific settings structure\n // Note: The indirection allows central control of many interpolants.\n\n // --- Protected interface\n\n DefaultSettings_: {},\n\n getSettings_: function () {\n\n return this.settings || this.DefaultSettings_;\n\n },\n\n copySampleValue_: function ( index ) {\n\n // copies a sample value to the result buffer\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n offset = index * stride;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] = values[ offset + i ];\n\n }\n\n return result;\n\n },\n\n // Template methods for derived classes:\n\n interpolate_: function ( /* i1, t0, t, t1 */ ) {\n\n throw new Error( 'call to abstract method' );\n // implementations shall return this.resultBuffer\n\n },\n\n intervalChanged_: function ( /* i1, t0, t1 */ ) {\n\n // empty\n\n }\n\n } );\n\n //!\\ DECLARE ALIAS AFTER assign prototype !\n Object.assign( Interpolant.prototype, {\n\n //( 0, t, t0 ), returns this.resultBuffer\n beforeStart_: Interpolant.prototype.copySampleValue_,\n\n //( N-1, tN-1, t ), returns this.resultBuffer\n afterEnd_: Interpolant.prototype.copySampleValue_,\n\n } );\n\n /**\n * Spherical linear unit quaternion interpolant.\n *\n * @author tschw\n */\n\n function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: QuaternionLinearInterpolant,\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n offset = i1 * stride,\n\n alpha = ( t - t0 ) / ( t1 - t0 );\n\n for ( var end = offset + stride; offset !== end; offset += 4 ) {\n\n Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n *\n * A Track of quaternion keyframe values.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function QuaternionKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: QuaternionKeyframeTrack,\n\n ValueTypeName: 'quaternion',\n\n // ValueBufferType is inherited\n\n DefaultInterpolation: InterpolateLinear,\n\n InterpolantFactoryMethodLinear: function ( result ) {\n\n return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodSmooth: undefined // not yet implemented\n\n } );\n\n /**\n *\n * A Track of keyframe values that represent color.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function ColorKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: ColorKeyframeTrack,\n\n ValueTypeName: 'color'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n // Note: Very basic implementation and nothing special yet.\n // However, this is the place for color space parameterization.\n\n } );\n\n /**\n *\n * A Track of numeric keyframe values.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function NumberKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: NumberKeyframeTrack,\n\n ValueTypeName: 'number'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n } );\n\n /**\n * Fast and simple cubic spline interpolant.\n *\n * It was derived from a Hermitian construction setting the first derivative\n * at each sample position to the linear slope between neighboring positions\n * over their parameter interval.\n *\n * @author tschw\n */\n\n function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n this._weightPrev = - 0;\n this._offsetPrev = - 0;\n this._weightNext = - 0;\n this._offsetNext = - 0;\n\n }\n\n CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: CubicInterpolant,\n\n DefaultSettings_: {\n\n endingStart: ZeroCurvatureEnding,\n endingEnd: ZeroCurvatureEnding\n\n },\n\n intervalChanged_: function ( i1, t0, t1 ) {\n\n var pp = this.parameterPositions,\n iPrev = i1 - 2,\n iNext = i1 + 1,\n\n tPrev = pp[ iPrev ],\n tNext = pp[ iNext ];\n\n if ( tPrev === undefined ) {\n\n switch ( this.getSettings_().endingStart ) {\n\n case ZeroSlopeEnding:\n\n // f'(t0) = 0\n iPrev = i1;\n tPrev = 2 * t0 - t1;\n\n break;\n\n case WrapAroundEnding:\n\n // use the other end of the curve\n iPrev = pp.length - 2;\n tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];\n\n break;\n\n default: // ZeroCurvatureEnding\n\n // f''(t0) = 0 a.k.a. Natural Spline\n iPrev = i1;\n tPrev = t1;\n\n }\n\n }\n\n if ( tNext === undefined ) {\n\n switch ( this.getSettings_().endingEnd ) {\n\n case ZeroSlopeEnding:\n\n // f'(tN) = 0\n iNext = i1;\n tNext = 2 * t1 - t0;\n\n break;\n\n case WrapAroundEnding:\n\n // use the other end of the curve\n iNext = 1;\n tNext = t1 + pp[ 1 ] - pp[ 0 ];\n\n break;\n\n default: // ZeroCurvatureEnding\n\n // f''(tN) = 0, a.k.a. Natural Spline\n iNext = i1 - 1;\n tNext = t0;\n\n }\n\n }\n\n var halfDt = ( t1 - t0 ) * 0.5,\n stride = this.valueSize;\n\n this._weightPrev = halfDt / ( t0 - tPrev );\n this._weightNext = halfDt / ( tNext - t1 );\n this._offsetPrev = iPrev * stride;\n this._offsetNext = iNext * stride;\n\n },\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n o1 = i1 * stride, o0 = o1 - stride,\n oP = this._offsetPrev, oN = this._offsetNext,\n wP = this._weightPrev, wN = this._weightNext,\n\n p = ( t - t0 ) / ( t1 - t0 ),\n pp = p * p,\n ppp = pp * p;\n\n // evaluate polynomials\n\n var sP = - wP * ppp + 2 * wP * pp - wP * p;\n var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;\n var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;\n var sN = wN * ppp - wN * pp;\n\n // combine data linearly\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] =\n sP * values[ oP + i ] +\n s0 * values[ o0 + i ] +\n s1 * values[ o1 + i ] +\n sN * values[ oN + i ];\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n * @author tschw\n */\n\n function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: LinearInterpolant,\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n offset1 = i1 * stride,\n offset0 = offset1 - stride,\n\n weight1 = ( t - t0 ) / ( t1 - t0 ),\n weight0 = 1 - weight1;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] =\n values[ offset0 + i ] * weight0 +\n values[ offset1 + i ] * weight1;\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n *\n * Interpolant that evaluates to the sample value at the position preceeding\n * the parameter.\n *\n * @author tschw\n */\n\n function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: DiscreteInterpolant,\n\n interpolate_: function ( i1 /*, t0, t, t1 */ ) {\n\n return this.copySampleValue_( i1 - 1 );\n\n }\n\n } );\n\n /**\n * @author tschw\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n */\n\n var AnimationUtils = {\n\n // same as Array.prototype.slice, but also works on typed arrays\n arraySlice: function ( array, from, to ) {\n\n if ( AnimationUtils.isTypedArray( array ) ) {\n\n // in ios9 array.subarray(from, undefined) will return empty array\n // but array.subarray(from) or array.subarray(from, len) is correct\n return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );\n\n }\n\n return array.slice( from, to );\n\n },\n\n // converts an array to a specific type\n convertArray: function ( array, type, forceClone ) {\n\n if ( ! array || // let 'undefined' and 'null' pass\n ! forceClone && array.constructor === type ) return array;\n\n if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {\n\n return new type( array ); // create typed array\n\n }\n\n return Array.prototype.slice.call( array ); // create Array\n\n },\n\n isTypedArray: function ( object ) {\n\n return ArrayBuffer.isView( object ) &&\n ! ( object instanceof DataView );\n\n },\n\n // returns an array by which times and values can be sorted\n getKeyframeOrder: function ( times ) {\n\n function compareTime( i, j ) {\n\n return times[ i ] - times[ j ];\n\n }\n\n var n = times.length;\n var result = new Array( n );\n for ( var i = 0; i !== n; ++ i ) result[ i ] = i;\n\n result.sort( compareTime );\n\n return result;\n\n },\n\n // uses the array previously returned by 'getKeyframeOrder' to sort data\n sortedArray: function ( values, stride, order ) {\n\n var nValues = values.length;\n var result = new values.constructor( nValues );\n\n for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {\n\n var srcOffset = order[ i ] * stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n result[ dstOffset ++ ] = values[ srcOffset + j ];\n\n }\n\n }\n\n return result;\n\n },\n\n // function for parsing AOS keyframe formats\n flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {\n\n var i = 1, key = jsonKeys[ 0 ];\n\n while ( key !== undefined && key[ valuePropertyName ] === undefined ) {\n\n key = jsonKeys[ i ++ ];\n\n }\n\n if ( key === undefined ) return; // no data\n\n var value = key[ valuePropertyName ];\n if ( value === undefined ) return; // no data\n\n if ( Array.isArray( value ) ) {\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n values.push.apply( values, value ); // push all elements\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n } else if ( value.toArray !== undefined ) {\n\n // ...assume THREE.Math-ish\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n value.toArray( values, values.length );\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n } else {\n\n // otherwise push as-is\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n values.push( value );\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n }\n\n }\n\n };\n\n /**\n *\n * A timed sequence of keyframes for a specific property.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function KeyframeTrack( name, times, values, interpolation ) {\n\n if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );\n if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );\n\n this.name = name;\n\n this.times = AnimationUtils.convertArray( times, this.TimeBufferType );\n this.values = AnimationUtils.convertArray( values, this.ValueBufferType );\n\n this.setInterpolation( interpolation || this.DefaultInterpolation );\n\n this.validate();\n this.optimize();\n\n }\n\n // Static methods:\n\n Object.assign( KeyframeTrack, {\n\n // Serialization (in static context, because of constructor invocation\n // and automatic invocation of .toJSON):\n\n parse: function ( json ) {\n\n if ( json.type === undefined ) {\n\n throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );\n\n }\n\n var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );\n\n if ( json.times === undefined ) {\n\n var times = [], values = [];\n\n AnimationUtils.flattenJSON( json.keys, times, values, 'value' );\n\n json.times = times;\n json.values = values;\n\n }\n\n // derived classes can define a static parse method\n if ( trackType.parse !== undefined ) {\n\n return trackType.parse( json );\n\n } else {\n\n // by default, we assume a constructor compatible with the base\n return new trackType( json.name, json.times, json.values, json.interpolation );\n\n }\n\n },\n\n toJSON: function ( track ) {\n\n var trackType = track.constructor;\n\n var json;\n\n // derived classes can define a static toJSON method\n if ( trackType.toJSON !== undefined ) {\n\n json = trackType.toJSON( track );\n\n } else {\n\n // by default, we assume the data can be serialized as-is\n json = {\n\n 'name': track.name,\n 'times': AnimationUtils.convertArray( track.times, Array ),\n 'values': AnimationUtils.convertArray( track.values, Array )\n\n };\n\n var interpolation = track.getInterpolation();\n\n if ( interpolation !== track.DefaultInterpolation ) {\n\n json.interpolation = interpolation;\n\n }\n\n }\n\n json.type = track.ValueTypeName; // mandatory\n\n return json;\n\n },\n\n _getTrackTypeForValueTypeName: function ( typeName ) {\n\n switch ( typeName.toLowerCase() ) {\n\n case 'scalar':\n case 'double':\n case 'float':\n case 'number':\n case 'integer':\n\n return NumberKeyframeTrack;\n\n case 'vector':\n case 'vector2':\n case 'vector3':\n case 'vector4':\n\n return VectorKeyframeTrack;\n\n case 'color':\n\n return ColorKeyframeTrack;\n\n case 'quaternion':\n\n return QuaternionKeyframeTrack;\n\n case 'bool':\n case 'boolean':\n\n return BooleanKeyframeTrack;\n\n case 'string':\n\n return StringKeyframeTrack;\n\n }\n\n throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );\n\n }\n\n } );\n\n Object.assign( KeyframeTrack.prototype, {\n\n constructor: KeyframeTrack,\n\n TimeBufferType: Float32Array,\n\n ValueBufferType: Float32Array,\n\n DefaultInterpolation: InterpolateLinear,\n\n InterpolantFactoryMethodDiscrete: function ( result ) {\n\n return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodLinear: function ( result ) {\n\n return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodSmooth: function ( result ) {\n\n return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n setInterpolation: function ( interpolation ) {\n\n var factoryMethod;\n\n switch ( interpolation ) {\n\n case InterpolateDiscrete:\n\n factoryMethod = this.InterpolantFactoryMethodDiscrete;\n\n break;\n\n case InterpolateLinear:\n\n factoryMethod = this.InterpolantFactoryMethodLinear;\n\n break;\n\n case InterpolateSmooth:\n\n factoryMethod = this.InterpolantFactoryMethodSmooth;\n\n break;\n\n }\n\n if ( factoryMethod === undefined ) {\n\n var message = \"unsupported interpolation for \" +\n this.ValueTypeName + \" keyframe track named \" + this.name;\n\n if ( this.createInterpolant === undefined ) {\n\n // fall back to default, unless the default itself is messed up\n if ( interpolation !== this.DefaultInterpolation ) {\n\n this.setInterpolation( this.DefaultInterpolation );\n\n } else {\n\n throw new Error( message ); // fatal, in this case\n\n }\n\n }\n\n console.warn( 'THREE.KeyframeTrack:', message );\n return;\n\n }\n\n this.createInterpolant = factoryMethod;\n\n },\n\n getInterpolation: function () {\n\n switch ( this.createInterpolant ) {\n\n case this.InterpolantFactoryMethodDiscrete:\n\n return InterpolateDiscrete;\n\n case this.InterpolantFactoryMethodLinear:\n\n return InterpolateLinear;\n\n case this.InterpolantFactoryMethodSmooth:\n\n return InterpolateSmooth;\n\n }\n\n },\n\n getValueSize: function () {\n\n return this.values.length / this.times.length;\n\n },\n\n // move all keyframes either forwards or backwards in time\n shift: function ( timeOffset ) {\n\n if ( timeOffset !== 0.0 ) {\n\n var times = this.times;\n\n for ( var i = 0, n = times.length; i !== n; ++ i ) {\n\n times[ i ] += timeOffset;\n\n }\n\n }\n\n return this;\n\n },\n\n // scale all keyframe times by a factor (useful for frame <-> seconds conversions)\n scale: function ( timeScale ) {\n\n if ( timeScale !== 1.0 ) {\n\n var times = this.times;\n\n for ( var i = 0, n = times.length; i !== n; ++ i ) {\n\n times[ i ] *= timeScale;\n\n }\n\n }\n\n return this;\n\n },\n\n // removes keyframes before and after animation without changing any values within the range [startTime, endTime].\n // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values\n trim: function ( startTime, endTime ) {\n\n var times = this.times,\n nKeys = times.length,\n from = 0,\n to = nKeys - 1;\n\n while ( from !== nKeys && times[ from ] < startTime ) {\n\n ++ from;\n\n }\n\n while ( to !== - 1 && times[ to ] > endTime ) {\n\n -- to;\n\n }\n\n ++ to; // inclusive -> exclusive bound\n\n if ( from !== 0 || to !== nKeys ) {\n\n // empty tracks are forbidden, so keep at least one keyframe\n if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;\n\n var stride = this.getValueSize();\n this.times = AnimationUtils.arraySlice( times, from, to );\n this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );\n\n }\n\n return this;\n\n },\n\n // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable\n validate: function () {\n\n var valid = true;\n\n var valueSize = this.getValueSize();\n if ( valueSize - Math.floor( valueSize ) !== 0 ) {\n\n console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );\n valid = false;\n\n }\n\n var times = this.times,\n values = this.values,\n\n nKeys = times.length;\n\n if ( nKeys === 0 ) {\n\n console.error( 'THREE.KeyframeTrack: Track is empty.', this );\n valid = false;\n\n }\n\n var prevTime = null;\n\n for ( var i = 0; i !== nKeys; i ++ ) {\n\n var currTime = times[ i ];\n\n if ( typeof currTime === 'number' && isNaN( currTime ) ) {\n\n console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );\n valid = false;\n break;\n\n }\n\n if ( prevTime !== null && prevTime > currTime ) {\n\n console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );\n valid = false;\n break;\n\n }\n\n prevTime = currTime;\n\n }\n\n if ( values !== undefined ) {\n\n if ( AnimationUtils.isTypedArray( values ) ) {\n\n for ( var i = 0, n = values.length; i !== n; ++ i ) {\n\n var value = values[ i ];\n\n if ( isNaN( value ) ) {\n\n console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );\n valid = false;\n break;\n\n }\n\n }\n\n }\n\n }\n\n return valid;\n\n },\n\n // removes equivalent sequential keys as common in morph target sequences\n // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)\n optimize: function () {\n\n var times = this.times,\n values = this.values,\n stride = this.getValueSize(),\n\n smoothInterpolation = this.getInterpolation() === InterpolateSmooth,\n\n writeIndex = 1,\n lastIndex = times.length - 1;\n\n for ( var i = 1; i < lastIndex; ++ i ) {\n\n var keep = false;\n\n var time = times[ i ];\n var timeNext = times[ i + 1 ];\n\n // remove adjacent keyframes scheduled at the same time\n\n if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {\n\n if ( ! smoothInterpolation ) {\n\n // remove unnecessary keyframes same as their neighbors\n\n var offset = i * stride,\n offsetP = offset - stride,\n offsetN = offset + stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n var value = values[ offset + j ];\n\n if ( value !== values[ offsetP + j ] ||\n value !== values[ offsetN + j ] ) {\n\n keep = true;\n break;\n\n }\n\n }\n\n } else {\n\n keep = true;\n\n }\n\n }\n\n // in-place compaction\n\n if ( keep ) {\n\n if ( i !== writeIndex ) {\n\n times[ writeIndex ] = times[ i ];\n\n var readOffset = i * stride,\n writeOffset = writeIndex * stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n values[ writeOffset + j ] = values[ readOffset + j ];\n\n }\n\n }\n\n ++ writeIndex;\n\n }\n\n }\n\n // flush last keyframe (compaction looks ahead)\n\n if ( lastIndex > 0 ) {\n\n times[ writeIndex ] = times[ lastIndex ];\n\n for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {\n\n values[ writeOffset + j ] = values[ readOffset + j ];\n\n }\n\n ++ writeIndex;\n\n }\n\n if ( writeIndex !== times.length ) {\n\n this.times = AnimationUtils.arraySlice( times, 0, writeIndex );\n this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n *\n * A Track of vectored keyframe values.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function VectorKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: VectorKeyframeTrack,\n\n ValueTypeName: 'vector'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n } );\n\n /**\n *\n * Reusable set of Tracks that represent an animation.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n */\n\n function AnimationClip( name, duration, tracks ) {\n\n this.name = name;\n this.tracks = tracks;\n this.duration = ( duration !== undefined ) ? duration : - 1;\n\n this.uuid = _Math.generateUUID();\n\n // this means it should figure out its duration by scanning the tracks\n if ( this.duration < 0 ) {\n\n this.resetDuration();\n\n }\n\n this.optimize();\n\n }\n\n Object.assign( AnimationClip, {\n\n parse: function ( json ) {\n\n var tracks = [],\n jsonTracks = json.tracks,\n frameTime = 1.0 / ( json.fps || 1.0 );\n\n for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) {\n\n tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) );\n\n }\n\n return new AnimationClip( json.name, json.duration, tracks );\n\n },\n\n toJSON: function ( clip ) {\n\n var tracks = [],\n clipTracks = clip.tracks;\n\n var json = {\n\n 'name': clip.name,\n 'duration': clip.duration,\n 'tracks': tracks\n\n };\n\n for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) {\n\n tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );\n\n }\n\n return json;\n\n },\n\n CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {\n\n var numMorphTargets = morphTargetSequence.length;\n var tracks = [];\n\n for ( var i = 0; i < numMorphTargets; i ++ ) {\n\n var times = [];\n var values = [];\n\n times.push(\n ( i + numMorphTargets - 1 ) % numMorphTargets,\n i,\n ( i + 1 ) % numMorphTargets );\n\n values.push( 0, 1, 0 );\n\n var order = AnimationUtils.getKeyframeOrder( times );\n times = AnimationUtils.sortedArray( times, 1, order );\n values = AnimationUtils.sortedArray( values, 1, order );\n\n // if there is a key at the first frame, duplicate it as the\n // last frame as well for perfect loop.\n if ( ! noLoop && times[ 0 ] === 0 ) {\n\n times.push( numMorphTargets );\n values.push( values[ 0 ] );\n\n }\n\n tracks.push(\n new NumberKeyframeTrack(\n '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',\n times, values\n ).scale( 1.0 / fps ) );\n\n }\n\n return new AnimationClip( name, - 1, tracks );\n\n },\n\n findByName: function ( objectOrClipArray, name ) {\n\n var clipArray = objectOrClipArray;\n\n if ( ! Array.isArray( objectOrClipArray ) ) {\n\n var o = objectOrClipArray;\n clipArray = o.geometry && o.geometry.animations || o.animations;\n\n }\n\n for ( var i = 0; i < clipArray.length; i ++ ) {\n\n if ( clipArray[ i ].name === name ) {\n\n return clipArray[ i ];\n\n }\n\n }\n\n return null;\n\n },\n\n CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {\n\n var animationToMorphTargets = {};\n\n // tested with https://regex101.com/ on trick sequences\n // such flamingo_flyA_003, flamingo_run1_003, crdeath0059\n var pattern = /^([\\w-]*?)([\\d]+)$/;\n\n // sort morph target names into animation groups based\n // patterns like Walk_001, Walk_002, Run_001, Run_002\n for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n var morphTarget = morphTargets[ i ];\n var parts = morphTarget.name.match( pattern );\n\n if ( parts && parts.length > 1 ) {\n\n var name = parts[ 1 ];\n\n var animationMorphTargets = animationToMorphTargets[ name ];\n if ( ! animationMorphTargets ) {\n\n animationToMorphTargets[ name ] = animationMorphTargets = [];\n\n }\n\n animationMorphTargets.push( morphTarget );\n\n }\n\n }\n\n var clips = [];\n\n for ( var name in animationToMorphTargets ) {\n\n clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );\n\n }\n\n return clips;\n\n },\n\n // parse the animation.hierarchy format\n parseAnimation: function ( animation, bones ) {\n\n if ( ! animation ) {\n\n console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' );\n return null;\n\n }\n\n var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {\n\n // only return track if there are actually keys.\n if ( animationKeys.length !== 0 ) {\n\n var times = [];\n var values = [];\n\n AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );\n\n // empty keys are filtered out, so check again\n if ( times.length !== 0 ) {\n\n destTracks.push( new trackType( trackName, times, values ) );\n\n }\n\n }\n\n };\n\n var tracks = [];\n\n var clipName = animation.name || 'default';\n // automatic length determination in AnimationClip.\n var duration = animation.length || - 1;\n var fps = animation.fps || 30;\n\n var hierarchyTracks = animation.hierarchy || [];\n\n for ( var h = 0; h < hierarchyTracks.length; h ++ ) {\n\n var animationKeys = hierarchyTracks[ h ].keys;\n\n // skip empty tracks\n if ( ! animationKeys || animationKeys.length === 0 ) continue;\n\n // process morph targets\n if ( animationKeys[ 0 ].morphTargets ) {\n\n // figure out all morph targets used in this track\n var morphTargetNames = {};\n\n for ( var k = 0; k < animationKeys.length; k ++ ) {\n\n if ( animationKeys[ k ].morphTargets ) {\n\n for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {\n\n morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;\n\n }\n\n }\n\n }\n\n // create a track for each morph target with all zero\n // morphTargetInfluences except for the keys in which\n // the morphTarget is named.\n for ( var morphTargetName in morphTargetNames ) {\n\n var times = [];\n var values = [];\n\n for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {\n\n var animationKey = animationKeys[ k ];\n\n times.push( animationKey.time );\n values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );\n\n }\n\n tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );\n\n }\n\n duration = morphTargetNames.length * ( fps || 1.0 );\n\n } else {\n\n // ...assume skeletal animation\n\n var boneName = '.bones[' + bones[ h ].name + ']';\n\n addNonemptyTrack(\n VectorKeyframeTrack, boneName + '.position',\n animationKeys, 'pos', tracks );\n\n addNonemptyTrack(\n QuaternionKeyframeTrack, boneName + '.quaternion',\n animationKeys, 'rot', tracks );\n\n addNonemptyTrack(\n VectorKeyframeTrack, boneName + '.scale',\n animationKeys, 'scl', tracks );\n\n }\n\n }\n\n if ( tracks.length === 0 ) {\n\n return null;\n\n }\n\n var clip = new AnimationClip( clipName, duration, tracks );\n\n return clip;\n\n }\n\n } );\n\n Object.assign( AnimationClip.prototype, {\n\n resetDuration: function () {\n\n var tracks = this.tracks, duration = 0;\n\n for ( var i = 0, n = tracks.length; i !== n; ++ i ) {\n\n var track = this.tracks[ i ];\n\n duration = Math.max( duration, track.times[ track.times.length - 1 ] );\n\n }\n\n this.duration = duration;\n\n },\n\n trim: function () {\n\n for ( var i = 0; i < this.tracks.length; i ++ ) {\n\n this.tracks[ i ].trim( 0, this.duration );\n\n }\n\n return this;\n\n },\n\n optimize: function () {\n\n for ( var i = 0; i < this.tracks.length; i ++ ) {\n\n this.tracks[ i ].optimize();\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function MaterialLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n this.textures = {};\n\n }\n\n Object.assign( MaterialLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n onLoad( scope.parse( JSON.parse( text ) ) );\n\n }, onProgress, onError );\n\n },\n\n setTextures: function ( value ) {\n\n this.textures = value;\n\n },\n\n parse: function ( json ) {\n\n var textures = this.textures;\n\n function getTexture( name ) {\n\n if ( textures[ name ] === undefined ) {\n\n console.warn( 'THREE.MaterialLoader: Undefined texture', name );\n\n }\n\n return textures[ name ];\n\n }\n\n var material = new Materials[ json.type ]();\n\n if ( json.uuid !== undefined ) material.uuid = json.uuid;\n if ( json.name !== undefined ) material.name = json.name;\n if ( json.color !== undefined ) material.color.setHex( json.color );\n if ( json.roughness !== undefined ) material.roughness = json.roughness;\n if ( json.metalness !== undefined ) material.metalness = json.metalness;\n if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );\n if ( json.specular !== undefined ) material.specular.setHex( json.specular );\n if ( json.shininess !== undefined ) material.shininess = json.shininess;\n if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat;\n if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness;\n if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;\n if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;\n if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;\n if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;\n if ( json.fog !== undefined ) material.fog = json.fog;\n if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;\n if ( json.blending !== undefined ) material.blending = json.blending;\n if ( json.side !== undefined ) material.side = json.side;\n if ( json.opacity !== undefined ) material.opacity = json.opacity;\n if ( json.transparent !== undefined ) material.transparent = json.transparent;\n if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;\n if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;\n if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;\n if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;\n if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;\n if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;\n if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;\n if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;\n\n if ( json.rotation !== undefined ) material.rotation = json.rotation;\n\n if ( json.linewidth !== 1 ) material.linewidth = json.linewidth;\n if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;\n if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;\n if ( json.scale !== undefined ) material.scale = json.scale;\n\n if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;\n if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;\n if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;\n\n if ( json.skinning !== undefined ) material.skinning = json.skinning;\n if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;\n if ( json.dithering !== undefined ) material.dithering = json.dithering;\n\n if ( json.visible !== undefined ) material.visible = json.visible;\n if ( json.userData !== undefined ) material.userData = json.userData;\n\n // Deprecated\n\n if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading\n\n // for PointsMaterial\n\n if ( json.size !== undefined ) material.size = json.size;\n if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;\n\n // maps\n\n if ( json.map !== undefined ) material.map = getTexture( json.map );\n\n if ( json.alphaMap !== undefined ) {\n\n material.alphaMap = getTexture( json.alphaMap );\n material.transparent = true;\n\n }\n\n if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );\n if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;\n\n if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );\n if ( json.normalScale !== undefined ) {\n\n var normalScale = json.normalScale;\n\n if ( Array.isArray( normalScale ) === false ) {\n\n // Blender exporter used to export a scalar. See #7459\n\n normalScale = [ normalScale, normalScale ];\n\n }\n\n material.normalScale = new Vector2().fromArray( normalScale );\n\n }\n\n if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );\n if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;\n if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;\n\n if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );\n if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );\n\n if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );\n if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;\n\n if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );\n\n if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );\n\n if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;\n\n if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );\n if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;\n\n if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );\n if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;\n\n if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );\n\n return material;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function BufferGeometryLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( BufferGeometryLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n onLoad( scope.parse( JSON.parse( text ) ) );\n\n }, onProgress, onError );\n\n },\n\n parse: function ( json ) {\n\n var geometry = new BufferGeometry();\n\n var index = json.data.index;\n\n if ( index !== undefined ) {\n\n var typedArray = new TYPED_ARRAYS[ index.type ]( index.array );\n geometry.setIndex( new BufferAttribute( typedArray, 1 ) );\n\n }\n\n var attributes = json.data.attributes;\n\n for ( var key in attributes ) {\n\n var attribute = attributes[ key ];\n var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array );\n\n geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) );\n\n }\n\n var groups = json.data.groups || json.data.drawcalls || json.data.offsets;\n\n if ( groups !== undefined ) {\n\n for ( var i = 0, n = groups.length; i !== n; ++ i ) {\n\n var group = groups[ i ];\n\n geometry.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n }\n\n var boundingSphere = json.data.boundingSphere;\n\n if ( boundingSphere !== undefined ) {\n\n var center = new Vector3();\n\n if ( boundingSphere.center !== undefined ) {\n\n center.fromArray( boundingSphere.center );\n\n }\n\n geometry.boundingSphere = new Sphere( center, boundingSphere.radius );\n\n }\n\n return geometry;\n\n }\n\n } );\n\n var TYPED_ARRAYS = {\n Int8Array: Int8Array,\n Uint8Array: Uint8Array,\n // Workaround for IE11 pre KB2929437. See #11440\n Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array,\n Int16Array: Int16Array,\n Uint16Array: Uint16Array,\n Int32Array: Int32Array,\n Uint32Array: Uint32Array,\n Float32Array: Float32Array,\n Float64Array: Float64Array\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Loader() {}\n\n Loader.Handlers = {\n\n handlers: [],\n\n add: function ( regex, loader ) {\n\n this.handlers.push( regex, loader );\n\n },\n\n get: function ( file ) {\n\n var handlers = this.handlers;\n\n for ( var i = 0, l = handlers.length; i < l; i += 2 ) {\n\n var regex = handlers[ i ];\n var loader = handlers[ i + 1 ];\n\n if ( regex.test( file ) ) {\n\n return loader;\n\n }\n\n }\n\n return null;\n\n }\n\n };\n\n Object.assign( Loader.prototype, {\n\n crossOrigin: undefined,\n\n onLoadStart: function () {},\n\n onLoadProgress: function () {},\n\n onLoadComplete: function () {},\n\n initMaterials: function ( materials, texturePath, crossOrigin ) {\n\n var array = [];\n\n for ( var i = 0; i < materials.length; ++ i ) {\n\n array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );\n\n }\n\n return array;\n\n },\n\n createMaterial: ( function () {\n\n var BlendingMode = {\n NoBlending: NoBlending,\n NormalBlending: NormalBlending,\n AdditiveBlending: AdditiveBlending,\n SubtractiveBlending: SubtractiveBlending,\n MultiplyBlending: MultiplyBlending,\n CustomBlending: CustomBlending\n };\n\n var color = new Color();\n var textureLoader = new TextureLoader();\n var materialLoader = new MaterialLoader();\n\n return function createMaterial( m, texturePath, crossOrigin ) {\n\n // convert from old material format\n\n var textures = {};\n\n function loadTexture( path, repeat, offset, wrap, anisotropy ) {\n\n var fullPath = texturePath + path;\n var loader = Loader.Handlers.get( fullPath );\n\n var texture;\n\n if ( loader !== null ) {\n\n texture = loader.load( fullPath );\n\n } else {\n\n textureLoader.setCrossOrigin( crossOrigin );\n texture = textureLoader.load( fullPath );\n\n }\n\n if ( repeat !== undefined ) {\n\n texture.repeat.fromArray( repeat );\n\n if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;\n if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;\n\n }\n\n if ( offset !== undefined ) {\n\n texture.offset.fromArray( offset );\n\n }\n\n if ( wrap !== undefined ) {\n\n if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;\n if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;\n\n if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;\n if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;\n\n }\n\n if ( anisotropy !== undefined ) {\n\n texture.anisotropy = anisotropy;\n\n }\n\n var uuid = _Math.generateUUID();\n\n textures[ uuid ] = texture;\n\n return uuid;\n\n }\n\n //\n\n var json = {\n uuid: _Math.generateUUID(),\n type: 'MeshLambertMaterial'\n };\n\n for ( var name in m ) {\n\n var value = m[ name ];\n\n switch ( name ) {\n\n case 'DbgColor':\n case 'DbgIndex':\n case 'opticalDensity':\n case 'illumination':\n break;\n case 'DbgName':\n json.name = value;\n break;\n case 'blending':\n json.blending = BlendingMode[ value ];\n break;\n case 'colorAmbient':\n case 'mapAmbient':\n console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );\n break;\n case 'colorDiffuse':\n json.color = color.fromArray( value ).getHex();\n break;\n case 'colorSpecular':\n json.specular = color.fromArray( value ).getHex();\n break;\n case 'colorEmissive':\n json.emissive = color.fromArray( value ).getHex();\n break;\n case 'specularCoef':\n json.shininess = value;\n break;\n case 'shading':\n if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';\n if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';\n if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';\n break;\n case 'mapDiffuse':\n json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );\n break;\n case 'mapDiffuseRepeat':\n case 'mapDiffuseOffset':\n case 'mapDiffuseWrap':\n case 'mapDiffuseAnisotropy':\n break;\n case 'mapEmissive':\n json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy );\n break;\n case 'mapEmissiveRepeat':\n case 'mapEmissiveOffset':\n case 'mapEmissiveWrap':\n case 'mapEmissiveAnisotropy':\n break;\n case 'mapLight':\n json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );\n break;\n case 'mapLightRepeat':\n case 'mapLightOffset':\n case 'mapLightWrap':\n case 'mapLightAnisotropy':\n break;\n case 'mapAO':\n json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );\n break;\n case 'mapAORepeat':\n case 'mapAOOffset':\n case 'mapAOWrap':\n case 'mapAOAnisotropy':\n break;\n case 'mapBump':\n json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );\n break;\n case 'mapBumpScale':\n json.bumpScale = value;\n break;\n case 'mapBumpRepeat':\n case 'mapBumpOffset':\n case 'mapBumpWrap':\n case 'mapBumpAnisotropy':\n break;\n case 'mapNormal':\n json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );\n break;\n case 'mapNormalFactor':\n json.normalScale = value;\n break;\n case 'mapNormalRepeat':\n case 'mapNormalOffset':\n case 'mapNormalWrap':\n case 'mapNormalAnisotropy':\n break;\n case 'mapSpecular':\n json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );\n break;\n case 'mapSpecularRepeat':\n case 'mapSpecularOffset':\n case 'mapSpecularWrap':\n case 'mapSpecularAnisotropy':\n break;\n case 'mapMetalness':\n json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy );\n break;\n case 'mapMetalnessRepeat':\n case 'mapMetalnessOffset':\n case 'mapMetalnessWrap':\n case 'mapMetalnessAnisotropy':\n break;\n case 'mapRoughness':\n json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy );\n break;\n case 'mapRoughnessRepeat':\n case 'mapRoughnessOffset':\n case 'mapRoughnessWrap':\n case 'mapRoughnessAnisotropy':\n break;\n case 'mapAlpha':\n json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );\n break;\n case 'mapAlphaRepeat':\n case 'mapAlphaOffset':\n case 'mapAlphaWrap':\n case 'mapAlphaAnisotropy':\n break;\n case 'flipSided':\n json.side = BackSide;\n break;\n case 'doubleSided':\n json.side = DoubleSide;\n break;\n case 'transparency':\n console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );\n json.opacity = value;\n break;\n case 'depthTest':\n case 'depthWrite':\n case 'colorWrite':\n case 'opacity':\n case 'reflectivity':\n case 'transparent':\n case 'visible':\n case 'wireframe':\n json[ name ] = value;\n break;\n case 'vertexColors':\n if ( value === true ) json.vertexColors = VertexColors;\n if ( value === 'face' ) json.vertexColors = FaceColors;\n break;\n default:\n console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );\n break;\n\n }\n\n }\n\n if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;\n if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;\n\n if ( json.opacity < 1 ) json.transparent = true;\n\n materialLoader.setTextures( textures );\n\n return materialLoader.parse( json );\n\n };\n\n } )()\n\n } );\n\n /**\n * @author Don McCurdy / https://www.donmccurdy.com\n */\n\n var LoaderUtils = {\n\n decodeText: function ( array ) {\n\n if ( typeof TextDecoder !== 'undefined' ) {\n\n return new TextDecoder().decode( array );\n\n }\n\n // Avoid the String.fromCharCode.apply(null, array) shortcut, which\n // throws a \"maximum call stack size exceeded\" error for large arrays.\n\n var s = '';\n\n for ( var i = 0, il = array.length; i < il; i ++ ) {\n\n // Implicitly assumes little-endian.\n s += String.fromCharCode( array[ i ] );\n\n }\n\n // Merges multi-byte utf-8 characters.\n return decodeURIComponent( escape( s ) );\n\n },\n\n extractUrlBase: function ( url ) {\n\n var parts = url.split( '/' );\n\n if ( parts.length === 1 ) return './';\n\n parts.pop();\n\n return parts.join( '/' ) + '/';\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function JSONLoader( manager ) {\n\n if ( typeof manager === 'boolean' ) {\n\n console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );\n manager = undefined;\n\n }\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n this.withCredentials = false;\n\n }\n\n Object.assign( JSONLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var texturePath = this.texturePath && ( typeof this.texturePath === 'string' ) ? this.texturePath : LoaderUtils.extractUrlBase( url );\n\n var loader = new FileLoader( this.manager );\n loader.setWithCredentials( this.withCredentials );\n loader.load( url, function ( text ) {\n\n var json = JSON.parse( text );\n var metadata = json.metadata;\n\n if ( metadata !== undefined ) {\n\n var type = metadata.type;\n\n if ( type !== undefined ) {\n\n if ( type.toLowerCase() === 'object' ) {\n\n console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );\n return;\n\n }\n\n }\n\n }\n\n var object = scope.parse( json, texturePath );\n onLoad( object.geometry, object.materials );\n\n }, onProgress, onError );\n\n },\n\n setTexturePath: function ( value ) {\n\n this.texturePath = value;\n\n },\n\n parse: ( function () {\n\n function parseModel( json, geometry ) {\n\n function isBitSet( value, position ) {\n\n return value & ( 1 << position );\n\n }\n\n var i, j, fi,\n\n offset, zLength,\n\n colorIndex, normalIndex, uvIndex, materialIndex,\n\n type,\n isQuad,\n hasMaterial,\n hasFaceVertexUv,\n hasFaceNormal, hasFaceVertexNormal,\n hasFaceColor, hasFaceVertexColor,\n\n vertex, face, faceA, faceB, hex, normal,\n\n uvLayer, uv, u, v,\n\n faces = json.faces,\n vertices = json.vertices,\n normals = json.normals,\n colors = json.colors,\n\n scale = json.scale,\n\n nUvLayers = 0;\n\n\n if ( json.uvs !== undefined ) {\n\n // disregard empty arrays\n\n for ( i = 0; i < json.uvs.length; i ++ ) {\n\n if ( json.uvs[ i ].length ) nUvLayers ++;\n\n }\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n geometry.faceVertexUvs[ i ] = [];\n\n }\n\n }\n\n offset = 0;\n zLength = vertices.length;\n\n while ( offset < zLength ) {\n\n vertex = new Vector3();\n\n vertex.x = vertices[ offset ++ ] * scale;\n vertex.y = vertices[ offset ++ ] * scale;\n vertex.z = vertices[ offset ++ ] * scale;\n\n geometry.vertices.push( vertex );\n\n }\n\n offset = 0;\n zLength = faces.length;\n\n while ( offset < zLength ) {\n\n type = faces[ offset ++ ];\n\n isQuad = isBitSet( type, 0 );\n hasMaterial = isBitSet( type, 1 );\n hasFaceVertexUv = isBitSet( type, 3 );\n hasFaceNormal = isBitSet( type, 4 );\n hasFaceVertexNormal = isBitSet( type, 5 );\n hasFaceColor = isBitSet( type, 6 );\n hasFaceVertexColor = isBitSet( type, 7 );\n\n // console.log(\"type\", type, \"bits\", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);\n\n if ( isQuad ) {\n\n faceA = new Face3();\n faceA.a = faces[ offset ];\n faceA.b = faces[ offset + 1 ];\n faceA.c = faces[ offset + 3 ];\n\n faceB = new Face3();\n faceB.a = faces[ offset + 1 ];\n faceB.b = faces[ offset + 2 ];\n faceB.c = faces[ offset + 3 ];\n\n offset += 4;\n\n if ( hasMaterial ) {\n\n materialIndex = faces[ offset ++ ];\n faceA.materialIndex = materialIndex;\n faceB.materialIndex = materialIndex;\n\n }\n\n // to get face <=> uv index correspondence\n\n fi = geometry.faces.length;\n\n if ( hasFaceVertexUv ) {\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n uvLayer = json.uvs[ i ];\n\n geometry.faceVertexUvs[ i ][ fi ] = [];\n geometry.faceVertexUvs[ i ][ fi + 1 ] = [];\n\n for ( j = 0; j < 4; j ++ ) {\n\n uvIndex = faces[ offset ++ ];\n\n u = uvLayer[ uvIndex * 2 ];\n v = uvLayer[ uvIndex * 2 + 1 ];\n\n uv = new Vector2( u, v );\n\n if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );\n if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );\n\n }\n\n }\n\n }\n\n if ( hasFaceNormal ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n faceA.normal.set(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n faceB.normal.copy( faceA.normal );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n for ( i = 0; i < 4; i ++ ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n normal = new Vector3(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n\n if ( i !== 2 ) faceA.vertexNormals.push( normal );\n if ( i !== 0 ) faceB.vertexNormals.push( normal );\n\n }\n\n }\n\n\n if ( hasFaceColor ) {\n\n colorIndex = faces[ offset ++ ];\n hex = colors[ colorIndex ];\n\n faceA.color.setHex( hex );\n faceB.color.setHex( hex );\n\n }\n\n\n if ( hasFaceVertexColor ) {\n\n for ( i = 0; i < 4; i ++ ) {\n\n colorIndex = faces[ offset ++ ];\n hex = colors[ colorIndex ];\n\n if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) );\n if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) );\n\n }\n\n }\n\n geometry.faces.push( faceA );\n geometry.faces.push( faceB );\n\n } else {\n\n face = new Face3();\n face.a = faces[ offset ++ ];\n face.b = faces[ offset ++ ];\n face.c = faces[ offset ++ ];\n\n if ( hasMaterial ) {\n\n materialIndex = faces[ offset ++ ];\n face.materialIndex = materialIndex;\n\n }\n\n // to get face <=> uv index correspondence\n\n fi = geometry.faces.length;\n\n if ( hasFaceVertexUv ) {\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n uvLayer = json.uvs[ i ];\n\n geometry.faceVertexUvs[ i ][ fi ] = [];\n\n for ( j = 0; j < 3; j ++ ) {\n\n uvIndex = faces[ offset ++ ];\n\n u = uvLayer[ uvIndex * 2 ];\n v = uvLayer[ uvIndex * 2 + 1 ];\n\n uv = new Vector2( u, v );\n\n geometry.faceVertexUvs[ i ][ fi ].push( uv );\n\n }\n\n }\n\n }\n\n if ( hasFaceNormal ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n face.normal.set(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n for ( i = 0; i < 3; i ++ ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n normal = new Vector3(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n face.vertexNormals.push( normal );\n\n }\n\n }\n\n\n if ( hasFaceColor ) {\n\n colorIndex = faces[ offset ++ ];\n face.color.setHex( colors[ colorIndex ] );\n\n }\n\n\n if ( hasFaceVertexColor ) {\n\n for ( i = 0; i < 3; i ++ ) {\n\n colorIndex = faces[ offset ++ ];\n face.vertexColors.push( new Color( colors[ colorIndex ] ) );\n\n }\n\n }\n\n geometry.faces.push( face );\n\n }\n\n }\n\n }\n\n function parseSkin( json, geometry ) {\n\n var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;\n\n if ( json.skinWeights ) {\n\n for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {\n\n var x = json.skinWeights[ i ];\n var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;\n var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;\n var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;\n\n geometry.skinWeights.push( new Vector4( x, y, z, w ) );\n\n }\n\n }\n\n if ( json.skinIndices ) {\n\n for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {\n\n var a = json.skinIndices[ i ];\n var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;\n var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;\n var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;\n\n geometry.skinIndices.push( new Vector4( a, b, c, d ) );\n\n }\n\n }\n\n geometry.bones = json.bones;\n\n if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {\n\n console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +\n geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );\n\n }\n\n }\n\n function parseMorphing( json, geometry ) {\n\n var scale = json.scale;\n\n if ( json.morphTargets !== undefined ) {\n\n for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {\n\n geometry.morphTargets[ i ] = {};\n geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;\n geometry.morphTargets[ i ].vertices = [];\n\n var dstVertices = geometry.morphTargets[ i ].vertices;\n var srcVertices = json.morphTargets[ i ].vertices;\n\n for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {\n\n var vertex = new Vector3();\n vertex.x = srcVertices[ v ] * scale;\n vertex.y = srcVertices[ v + 1 ] * scale;\n vertex.z = srcVertices[ v + 2 ] * scale;\n\n dstVertices.push( vertex );\n\n }\n\n }\n\n }\n\n if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {\n\n console.warn( 'THREE.JSONLoader: \"morphColors\" no longer supported. Using them as face colors.' );\n\n var faces = geometry.faces;\n var morphColors = json.morphColors[ 0 ].colors;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n faces[ i ].color.fromArray( morphColors, i * 3 );\n\n }\n\n }\n\n }\n\n function parseAnimations( json, geometry ) {\n\n var outputAnimations = [];\n\n // parse old style Bone/Hierarchy animations\n var animations = [];\n\n if ( json.animation !== undefined ) {\n\n animations.push( json.animation );\n\n }\n\n if ( json.animations !== undefined ) {\n\n if ( json.animations.length ) {\n\n animations = animations.concat( json.animations );\n\n } else {\n\n animations.push( json.animations );\n\n }\n\n }\n\n for ( var i = 0; i < animations.length; i ++ ) {\n\n var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones );\n if ( clip ) outputAnimations.push( clip );\n\n }\n\n // parse implicit morph animations\n if ( geometry.morphTargets ) {\n\n // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.\n var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );\n outputAnimations = outputAnimations.concat( morphAnimationClips );\n\n }\n\n if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;\n\n }\n\n return function parse( json, texturePath ) {\n\n if ( json.data !== undefined ) {\n\n // Geometry 4.0 spec\n json = json.data;\n\n }\n\n if ( json.scale !== undefined ) {\n\n json.scale = 1.0 / json.scale;\n\n } else {\n\n json.scale = 1.0;\n\n }\n\n var geometry = new Geometry();\n\n parseModel( json, geometry );\n parseSkin( json, geometry );\n parseMorphing( json, geometry );\n parseAnimations( json, geometry );\n\n geometry.computeFaceNormals();\n geometry.computeBoundingSphere();\n\n if ( json.materials === undefined || json.materials.length === 0 ) {\n\n return { geometry: geometry };\n\n } else {\n\n var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );\n\n return { geometry: geometry, materials: materials };\n\n }\n\n };\n\n } )()\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ObjectLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n this.texturePath = '';\n\n }\n\n Object.assign( ObjectLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( this.texturePath === '' ) {\n\n this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );\n\n }\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n var json = null;\n\n try {\n\n json = JSON.parse( text );\n\n } catch ( error ) {\n\n if ( onError !== undefined ) onError( error );\n\n console.error( 'THREE:ObjectLoader: Can\\'t parse ' + url + '.', error.message );\n\n return;\n\n }\n\n var metadata = json.metadata;\n\n if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {\n\n console.error( 'THREE.ObjectLoader: Can\\'t load ' + url + '. Use THREE.JSONLoader instead.' );\n return;\n\n }\n\n scope.parse( json, onLoad );\n\n }, onProgress, onError );\n\n },\n\n setTexturePath: function ( value ) {\n\n this.texturePath = value;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n\n },\n\n parse: function ( json, onLoad ) {\n\n var shapes = this.parseShape( json.shapes );\n var geometries = this.parseGeometries( json.geometries, shapes );\n\n var images = this.parseImages( json.images, function () {\n\n if ( onLoad !== undefined ) onLoad( object );\n\n } );\n\n var textures = this.parseTextures( json.textures, images );\n var materials = this.parseMaterials( json.materials, textures );\n\n var object = this.parseObject( json.object, geometries, materials );\n\n if ( json.animations ) {\n\n object.animations = this.parseAnimations( json.animations );\n\n }\n\n if ( json.images === undefined || json.images.length === 0 ) {\n\n if ( onLoad !== undefined ) onLoad( object );\n\n }\n\n return object;\n\n },\n\n parseShape: function ( json ) {\n\n var shapes = {};\n\n if ( json !== undefined ) {\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var shape = new Shape().fromJSON( json[ i ] );\n\n shapes[ shape.uuid ] = shape;\n\n }\n\n }\n\n return shapes;\n\n },\n\n parseGeometries: function ( json, shapes ) {\n\n var geometries = {};\n\n if ( json !== undefined ) {\n\n var geometryLoader = new JSONLoader();\n var bufferGeometryLoader = new BufferGeometryLoader();\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var geometry;\n var data = json[ i ];\n\n switch ( data.type ) {\n\n case 'PlaneGeometry':\n case 'PlaneBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.width,\n data.height,\n data.widthSegments,\n data.heightSegments\n );\n\n break;\n\n case 'BoxGeometry':\n case 'BoxBufferGeometry':\n case 'CubeGeometry': // backwards compatible\n\n geometry = new Geometries[ data.type ](\n data.width,\n data.height,\n data.depth,\n data.widthSegments,\n data.heightSegments,\n data.depthSegments\n );\n\n break;\n\n case 'CircleGeometry':\n case 'CircleBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.segments,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'CylinderGeometry':\n case 'CylinderBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radiusTop,\n data.radiusBottom,\n data.height,\n data.radialSegments,\n data.heightSegments,\n data.openEnded,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'ConeGeometry':\n case 'ConeBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.height,\n data.radialSegments,\n data.heightSegments,\n data.openEnded,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'SphereGeometry':\n case 'SphereBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.widthSegments,\n data.heightSegments,\n data.phiStart,\n data.phiLength,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'DodecahedronGeometry':\n case 'DodecahedronBufferGeometry':\n case 'IcosahedronGeometry':\n case 'IcosahedronBufferGeometry':\n case 'OctahedronGeometry':\n case 'OctahedronBufferGeometry':\n case 'TetrahedronGeometry':\n case 'TetrahedronBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.detail\n );\n\n break;\n\n case 'RingGeometry':\n case 'RingBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.innerRadius,\n data.outerRadius,\n data.thetaSegments,\n data.phiSegments,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'TorusGeometry':\n case 'TorusBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.tube,\n data.radialSegments,\n data.tubularSegments,\n data.arc\n );\n\n break;\n\n case 'TorusKnotGeometry':\n case 'TorusKnotBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.tube,\n data.tubularSegments,\n data.radialSegments,\n data.p,\n data.q\n );\n\n break;\n\n case 'LatheGeometry':\n case 'LatheBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.points,\n data.segments,\n data.phiStart,\n data.phiLength\n );\n\n break;\n\n case 'PolyhedronGeometry':\n case 'PolyhedronBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.vertices,\n data.indices,\n data.radius,\n data.details\n );\n\n break;\n\n case 'ShapeGeometry':\n case 'ShapeBufferGeometry':\n\n var geometryShapes = [];\n\n for ( var i = 0, l = data.shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ data.shapes[ i ] ];\n\n geometryShapes.push( shape );\n\n }\n\n geometry = new Geometries[ data.type ](\n geometryShapes,\n data.curveSegments\n );\n\n break;\n\n case 'BufferGeometry':\n\n geometry = bufferGeometryLoader.parse( data );\n\n break;\n\n case 'Geometry':\n\n geometry = geometryLoader.parse( data, this.texturePath ).geometry;\n\n break;\n\n default:\n\n console.warn( 'THREE.ObjectLoader: Unsupported geometry type \"' + data.type + '\"' );\n\n continue;\n\n }\n\n geometry.uuid = data.uuid;\n\n if ( data.name !== undefined ) geometry.name = data.name;\n\n geometries[ data.uuid ] = geometry;\n\n }\n\n }\n\n return geometries;\n\n },\n\n parseMaterials: function ( json, textures ) {\n\n var materials = {};\n\n if ( json !== undefined ) {\n\n var loader = new MaterialLoader();\n loader.setTextures( textures );\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var data = json[ i ];\n\n if ( data.type === 'MultiMaterial' ) {\n\n // Deprecated\n\n var array = [];\n\n for ( var j = 0; j < data.materials.length; j ++ ) {\n\n array.push( loader.parse( data.materials[ j ] ) );\n\n }\n\n materials[ data.uuid ] = array;\n\n } else {\n\n materials[ data.uuid ] = loader.parse( data );\n\n }\n\n }\n\n }\n\n return materials;\n\n },\n\n parseAnimations: function ( json ) {\n\n var animations = [];\n\n for ( var i = 0; i < json.length; i ++ ) {\n\n var clip = AnimationClip.parse( json[ i ] );\n\n animations.push( clip );\n\n }\n\n return animations;\n\n },\n\n parseImages: function ( json, onLoad ) {\n\n var scope = this;\n var images = {};\n\n function loadImage( url ) {\n\n scope.manager.itemStart( url );\n\n return loader.load( url, function () {\n\n scope.manager.itemEnd( url );\n\n }, undefined, function () {\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n } );\n\n }\n\n if ( json !== undefined && json.length > 0 ) {\n\n var manager = new LoadingManager( onLoad );\n\n var loader = new ImageLoader( manager );\n loader.setCrossOrigin( this.crossOrigin );\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var image = json[ i ];\n var path = /^(\\/\\/)|([a-z]+:(\\/\\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;\n\n images[ image.uuid ] = loadImage( path );\n\n }\n\n }\n\n return images;\n\n },\n\n parseTextures: function ( json, images ) {\n\n function parseConstant( value, type ) {\n\n if ( typeof value === 'number' ) return value;\n\n console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );\n\n return type[ value ];\n\n }\n\n var textures = {};\n\n if ( json !== undefined ) {\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var data = json[ i ];\n\n if ( data.image === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: No \"image\" specified for', data.uuid );\n\n }\n\n if ( images[ data.image ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined image', data.image );\n\n }\n\n var texture = new Texture( images[ data.image ] );\n texture.needsUpdate = true;\n\n texture.uuid = data.uuid;\n\n if ( data.name !== undefined ) texture.name = data.name;\n\n if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING );\n\n if ( data.offset !== undefined ) texture.offset.fromArray( data.offset );\n if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );\n if ( data.center !== undefined ) texture.center.fromArray( data.center );\n if ( data.rotation !== undefined ) texture.rotation = data.rotation;\n\n if ( data.wrap !== undefined ) {\n\n texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING );\n texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING );\n\n }\n\n if ( data.format !== undefined ) texture.format = data.format;\n\n if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER );\n if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER );\n if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;\n\n if ( data.flipY !== undefined ) texture.flipY = data.flipY;\n\n textures[ data.uuid ] = texture;\n\n }\n\n }\n\n return textures;\n\n },\n\n parseObject: function ( data, geometries, materials ) {\n\n var object;\n\n function getGeometry( name ) {\n\n if ( geometries[ name ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined geometry', name );\n\n }\n\n return geometries[ name ];\n\n }\n\n function getMaterial( name ) {\n\n if ( name === undefined ) return undefined;\n\n if ( Array.isArray( name ) ) {\n\n var array = [];\n\n for ( var i = 0, l = name.length; i < l; i ++ ) {\n\n var uuid = name[ i ];\n\n if ( materials[ uuid ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined material', uuid );\n\n }\n\n array.push( materials[ uuid ] );\n\n }\n\n return array;\n\n }\n\n if ( materials[ name ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined material', name );\n\n }\n\n return materials[ name ];\n\n }\n\n switch ( data.type ) {\n\n case 'Scene':\n\n object = new Scene();\n\n if ( data.background !== undefined ) {\n\n if ( Number.isInteger( data.background ) ) {\n\n object.background = new Color( data.background );\n\n }\n\n }\n\n if ( data.fog !== undefined ) {\n\n if ( data.fog.type === 'Fog' ) {\n\n object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );\n\n } else if ( data.fog.type === 'FogExp2' ) {\n\n object.fog = new FogExp2( data.fog.color, data.fog.density );\n\n }\n\n }\n\n break;\n\n case 'PerspectiveCamera':\n\n object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );\n\n if ( data.focus !== undefined ) object.focus = data.focus;\n if ( data.zoom !== undefined ) object.zoom = data.zoom;\n if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;\n if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;\n if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n break;\n\n case 'OrthographicCamera':\n\n object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );\n\n if ( data.zoom !== undefined ) object.zoom = data.zoom;\n if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n break;\n\n case 'AmbientLight':\n\n object = new AmbientLight( data.color, data.intensity );\n\n break;\n\n case 'DirectionalLight':\n\n object = new DirectionalLight( data.color, data.intensity );\n\n break;\n\n case 'PointLight':\n\n object = new PointLight( data.color, data.intensity, data.distance, data.decay );\n\n break;\n\n case 'RectAreaLight':\n\n object = new RectAreaLight( data.color, data.intensity, data.width, data.height );\n\n break;\n\n case 'SpotLight':\n\n object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );\n\n break;\n\n case 'HemisphereLight':\n\n object = new HemisphereLight( data.color, data.groundColor, data.intensity );\n\n break;\n\n case 'SkinnedMesh':\n\n console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' );\n\n case 'Mesh':\n\n var geometry = getGeometry( data.geometry );\n var material = getMaterial( data.material );\n\n if ( geometry.bones && geometry.bones.length > 0 ) {\n\n object = new SkinnedMesh( geometry, material );\n\n } else {\n\n object = new Mesh( geometry, material );\n\n }\n\n break;\n\n case 'LOD':\n\n object = new LOD();\n\n break;\n\n case 'Line':\n\n object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );\n\n break;\n\n case 'LineLoop':\n\n object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'LineSegments':\n\n object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'PointCloud':\n case 'Points':\n\n object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'Sprite':\n\n object = new Sprite( getMaterial( data.material ) );\n\n break;\n\n case 'Group':\n\n object = new Group();\n\n break;\n\n default:\n\n object = new Object3D();\n\n }\n\n object.uuid = data.uuid;\n\n if ( data.name !== undefined ) object.name = data.name;\n if ( data.matrix !== undefined ) {\n\n object.matrix.fromArray( data.matrix );\n object.matrix.decompose( object.position, object.quaternion, object.scale );\n\n } else {\n\n if ( data.position !== undefined ) object.position.fromArray( data.position );\n if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );\n if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );\n if ( data.scale !== undefined ) object.scale.fromArray( data.scale );\n\n }\n\n if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;\n if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;\n\n if ( data.shadow ) {\n\n if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;\n if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;\n if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );\n if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );\n\n }\n\n if ( data.visible !== undefined ) object.visible = data.visible;\n if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;\n if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;\n if ( data.userData !== undefined ) object.userData = data.userData;\n\n if ( data.children !== undefined ) {\n\n var children = data.children;\n\n for ( var i = 0; i < children.length; i ++ ) {\n\n object.add( this.parseObject( children[ i ], geometries, materials ) );\n\n }\n\n }\n\n if ( data.type === 'LOD' ) {\n\n var levels = data.levels;\n\n for ( var l = 0; l < levels.length; l ++ ) {\n\n var level = levels[ l ];\n var child = object.getObjectByProperty( 'uuid', level.object );\n\n if ( child !== undefined ) {\n\n object.addLevel( child, level.distance );\n\n }\n\n }\n\n }\n\n return object;\n\n }\n\n } );\n\n var TEXTURE_MAPPING = {\n UVMapping: UVMapping,\n CubeReflectionMapping: CubeReflectionMapping,\n CubeRefractionMapping: CubeRefractionMapping,\n EquirectangularReflectionMapping: EquirectangularReflectionMapping,\n EquirectangularRefractionMapping: EquirectangularRefractionMapping,\n SphericalReflectionMapping: SphericalReflectionMapping,\n CubeUVReflectionMapping: CubeUVReflectionMapping,\n CubeUVRefractionMapping: CubeUVRefractionMapping\n };\n\n var TEXTURE_WRAPPING = {\n RepeatWrapping: RepeatWrapping,\n ClampToEdgeWrapping: ClampToEdgeWrapping,\n MirroredRepeatWrapping: MirroredRepeatWrapping\n };\n\n var TEXTURE_FILTER = {\n NearestFilter: NearestFilter,\n NearestMipMapNearestFilter: NearestMipMapNearestFilter,\n NearestMipMapLinearFilter: NearestMipMapLinearFilter,\n LinearFilter: LinearFilter,\n LinearMipMapNearestFilter: LinearMipMapNearestFilter,\n LinearMipMapLinearFilter: LinearMipMapLinearFilter\n };\n\n /**\n * @author thespite / http://clicktorelease.com/\n */\n\n function ImageBitmapLoader( manager ) {\n\n if ( typeof createImageBitmap === 'undefined' ) {\n\n console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );\n\n }\n\n if ( typeof fetch === 'undefined' ) {\n\n console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' );\n\n }\n\n this.manager = manager !== undefined ? manager : DefaultLoadingManager;\n this.options = undefined;\n\n }\n\n ImageBitmapLoader.prototype = {\n\n constructor: ImageBitmapLoader,\n\n setOptions: function setOptions( options ) {\n\n this.options = options;\n\n return this;\n\n },\n\n load: function load( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n fetch( url ).then( function ( res ) {\n\n return res.blob();\n\n } ).then( function ( blob ) {\n\n return createImageBitmap( blob, scope.options );\n\n } ).then( function ( imageBitmap ) {\n\n Cache.add( url, imageBitmap );\n\n if ( onLoad ) onLoad( imageBitmap );\n\n scope.manager.itemEnd( url );\n\n } ).catch( function ( e ) {\n\n if ( onError ) onError( e );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n } );\n\n },\n\n setCrossOrigin: function ( /* value */ ) {\n\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * minimal class for proxing functions to Path. Replaces old \"extractSubpaths()\"\n **/\n\n function ShapePath() {\n\n this.type = 'ShapePath';\n\n this.subPaths = [];\n this.currentPath = null;\n\n }\n\n Object.assign( ShapePath.prototype, {\n\n moveTo: function ( x, y ) {\n\n this.currentPath = new Path();\n this.subPaths.push( this.currentPath );\n this.currentPath.moveTo( x, y );\n\n },\n\n lineTo: function ( x, y ) {\n\n this.currentPath.lineTo( x, y );\n\n },\n\n quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {\n\n this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );\n\n },\n\n bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );\n\n },\n\n splineThru: function ( pts ) {\n\n this.currentPath.splineThru( pts );\n\n },\n\n toShapes: function ( isCCW, noHoles ) {\n\n function toShapesNoHoles( inSubpaths ) {\n\n var shapes = [];\n\n for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {\n\n var tmpPath = inSubpaths[ i ];\n\n var tmpShape = new Shape();\n tmpShape.curves = tmpPath.curves;\n\n shapes.push( tmpShape );\n\n }\n\n return shapes;\n\n }\n\n function isPointInsidePolygon( inPt, inPolygon ) {\n\n var polyLen = inPolygon.length;\n\n // inPt on polygon contour => immediate success or\n // toggling of inside/outside at every single! intersection point of an edge\n // with the horizontal line through inPt, left of inPt\n // not counting lowerY endpoints of edges and whole edges on that line\n var inside = false;\n for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {\n\n var edgeLowPt = inPolygon[ p ];\n var edgeHighPt = inPolygon[ q ];\n\n var edgeDx = edgeHighPt.x - edgeLowPt.x;\n var edgeDy = edgeHighPt.y - edgeLowPt.y;\n\n if ( Math.abs( edgeDy ) > Number.EPSILON ) {\n\n // not parallel\n if ( edgeDy < 0 ) {\n\n edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;\n edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;\n\n }\n if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;\n\n if ( inPt.y === edgeLowPt.y ) {\n\n if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?\n // continue; // no intersection or edgeLowPt => doesn't count !!!\n\n } else {\n\n var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );\n if ( perpEdge === 0 ) return true; // inPt is on contour ?\n if ( perpEdge < 0 ) continue;\n inside = ! inside; // true intersection left of inPt\n\n }\n\n } else {\n\n // parallel or collinear\n if ( inPt.y !== edgeLowPt.y ) continue; // parallel\n // edge lies on the same horizontal line as inPt\n if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||\n ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !\n // continue;\n\n }\n\n }\n\n return inside;\n\n }\n\n var isClockWise = ShapeUtils.isClockWise;\n\n var subPaths = this.subPaths;\n if ( subPaths.length === 0 ) return [];\n\n if ( noHoles === true ) return toShapesNoHoles( subPaths );\n\n\n var solid, tmpPath, tmpShape, shapes = [];\n\n if ( subPaths.length === 1 ) {\n\n tmpPath = subPaths[ 0 ];\n tmpShape = new Shape();\n tmpShape.curves = tmpPath.curves;\n shapes.push( tmpShape );\n return shapes;\n\n }\n\n var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );\n holesFirst = isCCW ? ! holesFirst : holesFirst;\n\n // console.log(\"Holes first\", holesFirst);\n\n var betterShapeHoles = [];\n var newShapes = [];\n var newShapeHoles = [];\n var mainIdx = 0;\n var tmpPoints;\n\n newShapes[ mainIdx ] = undefined;\n newShapeHoles[ mainIdx ] = [];\n\n for ( var i = 0, l = subPaths.length; i < l; i ++ ) {\n\n tmpPath = subPaths[ i ];\n tmpPoints = tmpPath.getPoints();\n solid = isClockWise( tmpPoints );\n solid = isCCW ? ! solid : solid;\n\n if ( solid ) {\n\n if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;\n\n newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };\n newShapes[ mainIdx ].s.curves = tmpPath.curves;\n\n if ( holesFirst ) mainIdx ++;\n newShapeHoles[ mainIdx ] = [];\n\n //console.log('cw', i);\n\n } else {\n\n newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );\n\n //console.log('ccw', i);\n\n }\n\n }\n\n // only Holes? -> probably all Shapes with wrong orientation\n if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );\n\n\n if ( newShapes.length > 1 ) {\n\n var ambiguous = false;\n var toChange = [];\n\n for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n betterShapeHoles[ sIdx ] = [];\n\n }\n\n for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n var sho = newShapeHoles[ sIdx ];\n\n for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {\n\n var ho = sho[ hIdx ];\n var hole_unassigned = true;\n\n for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {\n\n if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {\n\n if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );\n if ( hole_unassigned ) {\n\n hole_unassigned = false;\n betterShapeHoles[ s2Idx ].push( ho );\n\n } else {\n\n ambiguous = true;\n\n }\n\n }\n\n }\n if ( hole_unassigned ) {\n\n betterShapeHoles[ sIdx ].push( ho );\n\n }\n\n }\n\n }\n // console.log(\"ambiguous: \", ambiguous);\n if ( toChange.length > 0 ) {\n\n // console.log(\"to change: \", toChange);\n if ( ! ambiguous ) newShapeHoles = betterShapeHoles;\n\n }\n\n }\n\n var tmpHoles;\n\n for ( var i = 0, il = newShapes.length; i < il; i ++ ) {\n\n tmpShape = newShapes[ i ].s;\n shapes.push( tmpShape );\n tmpHoles = newShapeHoles[ i ];\n\n for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {\n\n tmpShape.holes.push( tmpHoles[ j ].h );\n\n }\n\n }\n\n //console.log(\"shape\", shapes);\n\n return shapes;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Font( data ) {\n\n this.type = 'Font';\n\n this.data = data;\n\n }\n\n Object.assign( Font.prototype, {\n\n isFont: true,\n\n generateShapes: function ( text, size, divisions ) {\n\n if ( size === undefined ) size = 100;\n if ( divisions === undefined ) divisions = 4;\n\n var shapes = [];\n var paths = createPaths( text, size, divisions, this.data );\n\n for ( var p = 0, pl = paths.length; p < pl; p ++ ) {\n\n Array.prototype.push.apply( shapes, paths[ p ].toShapes() );\n\n }\n\n return shapes;\n\n }\n\n } );\n\n function createPaths( text, size, divisions, data ) {\n\n var chars = String( text ).split( '' );\n var scale = size / data.resolution;\n var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;\n\n var paths = [];\n\n var offsetX = 0, offsetY = 0;\n\n for ( var i = 0; i < chars.length; i ++ ) {\n\n var char = chars[ i ];\n\n if ( char === '\\n' ) {\n\n offsetX = 0;\n offsetY -= line_height;\n\n } else {\n\n var ret = createPath( char, divisions, scale, offsetX, offsetY, data );\n offsetX += ret.offsetX;\n paths.push( ret.path );\n\n }\n\n }\n\n return paths;\n\n }\n\n function createPath( char, divisions, scale, offsetX, offsetY, data ) {\n\n var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];\n\n if ( ! glyph ) return;\n\n var path = new ShapePath();\n\n var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;\n\n if ( glyph.o ) {\n\n var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );\n\n for ( var i = 0, l = outline.length; i < l; ) {\n\n var action = outline[ i ++ ];\n\n switch ( action ) {\n\n case 'm': // moveTo\n\n x = outline[ i ++ ] * scale + offsetX;\n y = outline[ i ++ ] * scale + offsetY;\n\n path.moveTo( x, y );\n\n break;\n\n case 'l': // lineTo\n\n x = outline[ i ++ ] * scale + offsetX;\n y = outline[ i ++ ] * scale + offsetY;\n\n path.lineTo( x, y );\n\n break;\n\n case 'q': // quadraticCurveTo\n\n cpx = outline[ i ++ ] * scale + offsetX;\n cpy = outline[ i ++ ] * scale + offsetY;\n cpx1 = outline[ i ++ ] * scale + offsetX;\n cpy1 = outline[ i ++ ] * scale + offsetY;\n\n path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );\n\n break;\n\n case 'b': // bezierCurveTo\n\n cpx = outline[ i ++ ] * scale + offsetX;\n cpy = outline[ i ++ ] * scale + offsetY;\n cpx1 = outline[ i ++ ] * scale + offsetX;\n cpy1 = outline[ i ++ ] * scale + offsetY;\n cpx2 = outline[ i ++ ] * scale + offsetX;\n cpy2 = outline[ i ++ ] * scale + offsetY;\n\n path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );\n\n break;\n\n }\n\n }\n\n }\n\n return { offsetX: glyph.ha * scale, path: path };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function FontLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( FontLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( this.manager );\n loader.setPath( this.path );\n loader.load( url, function ( text ) {\n\n var json;\n\n try {\n\n json = JSON.parse( text );\n\n } catch ( e ) {\n\n console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );\n json = JSON.parse( text.substring( 65, text.length - 2 ) );\n\n }\n\n var font = scope.parse( json );\n\n if ( onLoad ) onLoad( font );\n\n }, onProgress, onError );\n\n },\n\n parse: function ( json ) {\n\n return new Font( json );\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var context;\n\n var AudioContext = {\n\n getContext: function () {\n\n if ( context === undefined ) {\n\n context = new ( window.AudioContext || window.webkitAudioContext )();\n\n }\n\n return context;\n\n },\n\n setContext: function ( value ) {\n\n context = value;\n\n }\n\n };\n\n /**\n * @author Reece Aaron Lecrivain / http://reecenotes.com/\n */\n\n function AudioLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( AudioLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var loader = new FileLoader( this.manager );\n loader.setResponseType( 'arraybuffer' );\n loader.load( url, function ( buffer ) {\n\n var context = AudioContext.getContext();\n\n context.decodeAudioData( buffer, function ( audioBuffer ) {\n\n onLoad( audioBuffer );\n\n } );\n\n }, onProgress, onError );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function StereoCamera() {\n\n this.type = 'StereoCamera';\n\n this.aspect = 1;\n\n this.eyeSep = 0.064;\n\n this.cameraL = new PerspectiveCamera();\n this.cameraL.layers.enable( 1 );\n this.cameraL.matrixAutoUpdate = false;\n\n this.cameraR = new PerspectiveCamera();\n this.cameraR.layers.enable( 2 );\n this.cameraR.matrixAutoUpdate = false;\n\n }\n\n Object.assign( StereoCamera.prototype, {\n\n update: ( function () {\n\n var instance, focus, fov, aspect, near, far, zoom, eyeSep;\n\n var eyeRight = new Matrix4();\n var eyeLeft = new Matrix4();\n\n return function update( camera ) {\n\n var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov ||\n aspect !== camera.aspect * this.aspect || near !== camera.near ||\n far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep;\n\n if ( needsUpdate ) {\n\n instance = this;\n focus = camera.focus;\n fov = camera.fov;\n aspect = camera.aspect * this.aspect;\n near = camera.near;\n far = camera.far;\n zoom = camera.zoom;\n\n // Off-axis stereoscopic effect based on\n // http://paulbourke.net/stereographics/stereorender/\n\n var projectionMatrix = camera.projectionMatrix.clone();\n eyeSep = this.eyeSep / 2;\n var eyeSepOnProjection = eyeSep * near / focus;\n var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom;\n var xmin, xmax;\n\n // translate xOffset\n\n eyeLeft.elements[ 12 ] = - eyeSep;\n eyeRight.elements[ 12 ] = eyeSep;\n\n // for left eye\n\n xmin = - ymax * aspect + eyeSepOnProjection;\n xmax = ymax * aspect + eyeSepOnProjection;\n\n projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );\n projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n this.cameraL.projectionMatrix.copy( projectionMatrix );\n\n // for right eye\n\n xmin = - ymax * aspect - eyeSepOnProjection;\n xmax = ymax * aspect - eyeSepOnProjection;\n\n projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );\n projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n this.cameraR.projectionMatrix.copy( projectionMatrix );\n\n }\n\n this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft );\n this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight );\n\n };\n\n } )()\n\n } );\n\n /**\n * Camera for rendering cube maps\n * - renders scene into axis-aligned cube\n *\n * @author alteredq / http://alteredqualia.com/\n */\n\n function CubeCamera( near, far, cubeResolution ) {\n\n Object3D.call( this );\n\n this.type = 'CubeCamera';\n\n var fov = 90, aspect = 1;\n\n var cameraPX = new PerspectiveCamera( fov, aspect, near, far );\n cameraPX.up.set( 0, - 1, 0 );\n cameraPX.lookAt( new Vector3( 1, 0, 0 ) );\n this.add( cameraPX );\n\n var cameraNX = new PerspectiveCamera( fov, aspect, near, far );\n cameraNX.up.set( 0, - 1, 0 );\n cameraNX.lookAt( new Vector3( - 1, 0, 0 ) );\n this.add( cameraNX );\n\n var cameraPY = new PerspectiveCamera( fov, aspect, near, far );\n cameraPY.up.set( 0, 0, 1 );\n cameraPY.lookAt( new Vector3( 0, 1, 0 ) );\n this.add( cameraPY );\n\n var cameraNY = new PerspectiveCamera( fov, aspect, near, far );\n cameraNY.up.set( 0, 0, - 1 );\n cameraNY.lookAt( new Vector3( 0, - 1, 0 ) );\n this.add( cameraNY );\n\n var cameraPZ = new PerspectiveCamera( fov, aspect, near, far );\n cameraPZ.up.set( 0, - 1, 0 );\n cameraPZ.lookAt( new Vector3( 0, 0, 1 ) );\n this.add( cameraPZ );\n\n var cameraNZ = new PerspectiveCamera( fov, aspect, near, far );\n cameraNZ.up.set( 0, - 1, 0 );\n cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) );\n this.add( cameraNZ );\n\n var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter };\n\n this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options );\n this.renderTarget.texture.name = \"CubeCamera\";\n\n this.update = function ( renderer, scene ) {\n\n if ( this.parent === null ) this.updateMatrixWorld();\n\n var renderTarget = this.renderTarget;\n var generateMipmaps = renderTarget.texture.generateMipmaps;\n\n renderTarget.texture.generateMipmaps = false;\n\n renderTarget.activeCubeFace = 0;\n renderer.render( scene, cameraPX, renderTarget );\n\n renderTarget.activeCubeFace = 1;\n renderer.render( scene, cameraNX, renderTarget );\n\n renderTarget.activeCubeFace = 2;\n renderer.render( scene, cameraPY, renderTarget );\n\n renderTarget.activeCubeFace = 3;\n renderer.render( scene, cameraNY, renderTarget );\n\n renderTarget.activeCubeFace = 4;\n renderer.render( scene, cameraPZ, renderTarget );\n\n renderTarget.texture.generateMipmaps = generateMipmaps;\n\n renderTarget.activeCubeFace = 5;\n renderer.render( scene, cameraNZ, renderTarget );\n\n renderer.setRenderTarget( null );\n\n };\n\n this.clear = function ( renderer, color, depth, stencil ) {\n\n var renderTarget = this.renderTarget;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n renderTarget.activeCubeFace = i;\n renderer.setRenderTarget( renderTarget );\n\n renderer.clear( color, depth, stencil );\n\n }\n\n renderer.setRenderTarget( null );\n\n };\n\n }\n\n CubeCamera.prototype = Object.create( Object3D.prototype );\n CubeCamera.prototype.constructor = CubeCamera;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AudioListener() {\n\n Object3D.call( this );\n\n this.type = 'AudioListener';\n\n this.context = AudioContext.getContext();\n\n this.gain = this.context.createGain();\n this.gain.connect( this.context.destination );\n\n this.filter = null;\n\n }\n\n AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: AudioListener,\n\n getInput: function () {\n\n return this.gain;\n\n },\n\n removeFilter: function ( ) {\n\n if ( this.filter !== null ) {\n\n this.gain.disconnect( this.filter );\n this.filter.disconnect( this.context.destination );\n this.gain.connect( this.context.destination );\n this.filter = null;\n\n }\n\n },\n\n getFilter: function () {\n\n return this.filter;\n\n },\n\n setFilter: function ( value ) {\n\n if ( this.filter !== null ) {\n\n this.gain.disconnect( this.filter );\n this.filter.disconnect( this.context.destination );\n\n } else {\n\n this.gain.disconnect( this.context.destination );\n\n }\n\n this.filter = value;\n this.gain.connect( this.filter );\n this.filter.connect( this.context.destination );\n\n },\n\n getMasterVolume: function () {\n\n return this.gain.gain.value;\n\n },\n\n setMasterVolume: function ( value ) {\n\n this.gain.gain.value = value;\n\n },\n\n updateMatrixWorld: ( function () {\n\n var position = new Vector3();\n var quaternion = new Quaternion();\n var scale = new Vector3();\n\n var orientation = new Vector3();\n\n return function updateMatrixWorld( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n var listener = this.context.listener;\n var up = this.up;\n\n this.matrixWorld.decompose( position, quaternion, scale );\n\n orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );\n\n if ( listener.positionX ) {\n\n listener.positionX.setValueAtTime( position.x, this.context.currentTime );\n listener.positionY.setValueAtTime( position.y, this.context.currentTime );\n listener.positionZ.setValueAtTime( position.z, this.context.currentTime );\n listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime );\n listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime );\n listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime );\n listener.upX.setValueAtTime( up.x, this.context.currentTime );\n listener.upY.setValueAtTime( up.y, this.context.currentTime );\n listener.upZ.setValueAtTime( up.z, this.context.currentTime );\n\n } else {\n\n listener.setPosition( position.x, position.y, position.z );\n listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );\n\n }\n\n };\n\n } )()\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Reece Aaron Lecrivain / http://reecenotes.com/\n */\n\n function Audio( listener ) {\n\n Object3D.call( this );\n\n this.type = 'Audio';\n\n this.context = listener.context;\n\n this.gain = this.context.createGain();\n this.gain.connect( listener.getInput() );\n\n this.autoplay = false;\n\n this.buffer = null;\n this.loop = false;\n this.startTime = 0;\n this.offset = 0;\n this.playbackRate = 1;\n this.isPlaying = false;\n this.hasPlaybackControl = true;\n this.sourceType = 'empty';\n\n this.filters = [];\n\n }\n\n Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Audio,\n\n getOutput: function () {\n\n return this.gain;\n\n },\n\n setNodeSource: function ( audioNode ) {\n\n this.hasPlaybackControl = false;\n this.sourceType = 'audioNode';\n this.source = audioNode;\n this.connect();\n\n return this;\n\n },\n\n setBuffer: function ( audioBuffer ) {\n\n this.buffer = audioBuffer;\n this.sourceType = 'buffer';\n\n if ( this.autoplay ) this.play();\n\n return this;\n\n },\n\n play: function () {\n\n if ( this.isPlaying === true ) {\n\n console.warn( 'THREE.Audio: Audio is already playing.' );\n return;\n\n }\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n var source = this.context.createBufferSource();\n\n source.buffer = this.buffer;\n source.loop = this.loop;\n source.onended = this.onEnded.bind( this );\n source.playbackRate.setValueAtTime( this.playbackRate, this.startTime );\n this.startTime = this.context.currentTime;\n source.start( this.startTime, this.offset );\n\n this.isPlaying = true;\n\n this.source = source;\n\n return this.connect();\n\n },\n\n pause: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n if ( this.isPlaying === true ) {\n\n this.source.stop();\n this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate;\n this.isPlaying = false;\n\n }\n\n return this;\n\n },\n\n stop: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.source.stop();\n this.offset = 0;\n this.isPlaying = false;\n\n return this;\n\n },\n\n connect: function () {\n\n if ( this.filters.length > 0 ) {\n\n this.source.connect( this.filters[ 0 ] );\n\n for ( var i = 1, l = this.filters.length; i < l; i ++ ) {\n\n this.filters[ i - 1 ].connect( this.filters[ i ] );\n\n }\n\n this.filters[ this.filters.length - 1 ].connect( this.getOutput() );\n\n } else {\n\n this.source.connect( this.getOutput() );\n\n }\n\n return this;\n\n },\n\n disconnect: function () {\n\n if ( this.filters.length > 0 ) {\n\n this.source.disconnect( this.filters[ 0 ] );\n\n for ( var i = 1, l = this.filters.length; i < l; i ++ ) {\n\n this.filters[ i - 1 ].disconnect( this.filters[ i ] );\n\n }\n\n this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() );\n\n } else {\n\n this.source.disconnect( this.getOutput() );\n\n }\n\n return this;\n\n },\n\n getFilters: function () {\n\n return this.filters;\n\n },\n\n setFilters: function ( value ) {\n\n if ( ! value ) value = [];\n\n if ( this.isPlaying === true ) {\n\n this.disconnect();\n this.filters = value;\n this.connect();\n\n } else {\n\n this.filters = value;\n\n }\n\n return this;\n\n },\n\n getFilter: function () {\n\n return this.getFilters()[ 0 ];\n\n },\n\n setFilter: function ( filter ) {\n\n return this.setFilters( filter ? [ filter ] : [] );\n\n },\n\n setPlaybackRate: function ( value ) {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.playbackRate = value;\n\n if ( this.isPlaying === true ) {\n\n this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime );\n\n }\n\n return this;\n\n },\n\n getPlaybackRate: function () {\n\n return this.playbackRate;\n\n },\n\n onEnded: function () {\n\n this.isPlaying = false;\n\n },\n\n getLoop: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return false;\n\n }\n\n return this.loop;\n\n },\n\n setLoop: function ( value ) {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.loop = value;\n\n if ( this.isPlaying === true ) {\n\n this.source.loop = this.loop;\n\n }\n\n return this;\n\n },\n\n getVolume: function () {\n\n return this.gain.gain.value;\n\n },\n\n setVolume: function ( value ) {\n\n this.gain.gain.value = value;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function PositionalAudio( listener ) {\n\n Audio.call( this, listener );\n\n this.panner = this.context.createPanner();\n this.panner.connect( this.gain );\n\n }\n\n PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), {\n\n constructor: PositionalAudio,\n\n getOutput: function () {\n\n return this.panner;\n\n },\n\n getRefDistance: function () {\n\n return this.panner.refDistance;\n\n },\n\n setRefDistance: function ( value ) {\n\n this.panner.refDistance = value;\n\n },\n\n getRolloffFactor: function () {\n\n return this.panner.rolloffFactor;\n\n },\n\n setRolloffFactor: function ( value ) {\n\n this.panner.rolloffFactor = value;\n\n },\n\n getDistanceModel: function () {\n\n return this.panner.distanceModel;\n\n },\n\n setDistanceModel: function ( value ) {\n\n this.panner.distanceModel = value;\n\n },\n\n getMaxDistance: function () {\n\n return this.panner.maxDistance;\n\n },\n\n setMaxDistance: function ( value ) {\n\n this.panner.maxDistance = value;\n\n },\n\n updateMatrixWorld: ( function () {\n\n var position = new Vector3();\n\n return function updateMatrixWorld( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n position.setFromMatrixPosition( this.matrixWorld );\n\n this.panner.setPosition( position.x, position.y, position.z );\n\n };\n\n } )()\n\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AudioAnalyser( audio, fftSize ) {\n\n this.analyser = audio.context.createAnalyser();\n this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048;\n\n this.data = new Uint8Array( this.analyser.frequencyBinCount );\n\n audio.getOutput().connect( this.analyser );\n\n }\n\n Object.assign( AudioAnalyser.prototype, {\n\n getFrequencyData: function () {\n\n this.analyser.getByteFrequencyData( this.data );\n\n return this.data;\n\n },\n\n getAverageFrequency: function () {\n\n var value = 0, data = this.getFrequencyData();\n\n for ( var i = 0; i < data.length; i ++ ) {\n\n value += data[ i ];\n\n }\n\n return value / data.length;\n\n }\n\n } );\n\n /**\n *\n * Buffered scene graph property that allows weighted accumulation.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function PropertyMixer( binding, typeName, valueSize ) {\n\n this.binding = binding;\n this.valueSize = valueSize;\n\n var bufferType = Float64Array,\n mixFunction;\n\n switch ( typeName ) {\n\n case 'quaternion':\n mixFunction = this._slerp;\n break;\n\n case 'string':\n case 'bool':\n bufferType = Array;\n mixFunction = this._select;\n break;\n\n default:\n mixFunction = this._lerp;\n\n }\n\n this.buffer = new bufferType( valueSize * 4 );\n // layout: [ incoming | accu0 | accu1 | orig ]\n //\n // interpolators can use .buffer as their .result\n // the data then goes to 'incoming'\n //\n // 'accu0' and 'accu1' are used frame-interleaved for\n // the cumulative result and are compared to detect\n // changes\n //\n // 'orig' stores the original state of the property\n\n this._mixBufferRegion = mixFunction;\n\n this.cumulativeWeight = 0;\n\n this.useCount = 0;\n this.referenceCount = 0;\n\n }\n\n Object.assign( PropertyMixer.prototype, {\n\n // accumulate data in the 'incoming' region into 'accu<i>'\n accumulate: function ( accuIndex, weight ) {\n\n // note: happily accumulating nothing when weight = 0, the caller knows\n // the weight and shouldn't have made the call in the first place\n\n var buffer = this.buffer,\n stride = this.valueSize,\n offset = accuIndex * stride + stride,\n\n currentWeight = this.cumulativeWeight;\n\n if ( currentWeight === 0 ) {\n\n // accuN := incoming * weight\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n buffer[ offset + i ] = buffer[ i ];\n\n }\n\n currentWeight = weight;\n\n } else {\n\n // accuN := accuN + incoming * weight\n\n currentWeight += weight;\n var mix = weight / currentWeight;\n this._mixBufferRegion( buffer, offset, 0, mix, stride );\n\n }\n\n this.cumulativeWeight = currentWeight;\n\n },\n\n // apply the state of 'accu<i>' to the binding when accus differ\n apply: function ( accuIndex ) {\n\n var stride = this.valueSize,\n buffer = this.buffer,\n offset = accuIndex * stride + stride,\n\n weight = this.cumulativeWeight,\n\n binding = this.binding;\n\n this.cumulativeWeight = 0;\n\n if ( weight < 1 ) {\n\n // accuN := accuN + original * ( 1 - cumulativeWeight )\n\n var originalValueOffset = stride * 3;\n\n this._mixBufferRegion(\n buffer, offset, originalValueOffset, 1 - weight, stride );\n\n }\n\n for ( var i = stride, e = stride + stride; i !== e; ++ i ) {\n\n if ( buffer[ i ] !== buffer[ i + stride ] ) {\n\n // value has changed -> update scene graph\n\n binding.setValue( buffer, offset );\n break;\n\n }\n\n }\n\n },\n\n // remember the state of the bound property and copy it to both accus\n saveOriginalState: function () {\n\n var binding = this.binding;\n\n var buffer = this.buffer,\n stride = this.valueSize,\n\n originalValueOffset = stride * 3;\n\n binding.getValue( buffer, originalValueOffset );\n\n // accu[0..1] := orig -- initially detect changes against the original\n for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) {\n\n buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];\n\n }\n\n this.cumulativeWeight = 0;\n\n },\n\n // apply the state previously taken via 'saveOriginalState' to the binding\n restoreOriginalState: function () {\n\n var originalValueOffset = this.valueSize * 3;\n this.binding.setValue( this.buffer, originalValueOffset );\n\n },\n\n\n // mix functions\n\n _select: function ( buffer, dstOffset, srcOffset, t, stride ) {\n\n if ( t >= 0.5 ) {\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n buffer[ dstOffset + i ] = buffer[ srcOffset + i ];\n\n }\n\n }\n\n },\n\n _slerp: function ( buffer, dstOffset, srcOffset, t ) {\n\n Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );\n\n },\n\n _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {\n\n var s = 1 - t;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n var j = dstOffset + i;\n\n buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;\n\n }\n\n }\n\n } );\n\n /**\n *\n * A reference to a real property in the scene graph.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n // Characters [].:/ are reserved for track binding syntax.\n var RESERVED_CHARS_RE = '\\\\[\\\\]\\\\.:\\\\/';\n\n function Composite( targetGroup, path, optionalParsedPath ) {\n\n var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );\n\n this._targetGroup = targetGroup;\n this._bindings = targetGroup.subscribe_( path, parsedPath );\n\n }\n\n Object.assign( Composite.prototype, {\n\n getValue: function ( array, offset ) {\n\n this.bind(); // bind all binding\n\n var firstValidIndex = this._targetGroup.nCachedObjects_,\n binding = this._bindings[ firstValidIndex ];\n\n // and only call .getValue on the first\n if ( binding !== undefined ) binding.getValue( array, offset );\n\n },\n\n setValue: function ( array, offset ) {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].setValue( array, offset );\n\n }\n\n },\n\n bind: function () {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].bind();\n\n }\n\n },\n\n unbind: function () {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].unbind();\n\n }\n\n }\n\n } );\n\n\n function PropertyBinding( rootNode, path, parsedPath ) {\n\n this.path = path;\n this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );\n\n this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;\n\n this.rootNode = rootNode;\n\n }\n\n Object.assign( PropertyBinding, {\n\n Composite: Composite,\n\n create: function ( root, path, parsedPath ) {\n\n if ( ! ( root && root.isAnimationObjectGroup ) ) {\n\n return new PropertyBinding( root, path, parsedPath );\n\n } else {\n\n return new PropertyBinding.Composite( root, path, parsedPath );\n\n }\n\n },\n\n /**\n * Replaces spaces with underscores and removes unsupported characters from\n * node names, to ensure compatibility with parseTrackName().\n *\n * @param {string} name Node name to be sanitized.\n * @return {string}\n */\n sanitizeNodeName: ( function () {\n\n var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );\n\n return function sanitizeNodeName( name ) {\n\n return name.replace( /\\s/g, '_' ).replace( reservedRe, '' );\n\n };\n\n }() ),\n\n parseTrackName: function () {\n\n // Attempts to allow node names from any language. ES5's w regexp matches\n // only latin characters, and the unicode \\p{L} is not yet supported. So\n // instead, we exclude reserved characters and match everything else.\n var wordChar = '[^' + RESERVED_CHARS_RE + ']';\n var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\\\.', '' ) + ']';\n\n // Parent directories, delimited by '/' or ':'. Currently unused, but must\n // be matched to parse the rest of the track name.\n var directoryRe = /((?:WC+[\\/:])*)/.source.replace( 'WC', wordChar );\n\n // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.\n var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );\n\n // Object on target node, and accessor. May not contain reserved\n // characters. Accessor may contain any character except closing bracket.\n var objectRe = /(?:\\.(WC+)(?:\\[(.+)\\])?)?/.source.replace( 'WC', wordChar );\n\n // Property and accessor. May not contain reserved characters. Accessor may\n // contain any non-bracket characters.\n var propertyRe = /\\.(WC+)(?:\\[(.+)\\])?/.source.replace( 'WC', wordChar );\n\n var trackRe = new RegExp( ''\n + '^'\n + directoryRe\n + nodeRe\n + objectRe\n + propertyRe\n + '$'\n );\n\n var supportedObjectNames = [ 'material', 'materials', 'bones' ];\n\n return function parseTrackName( trackName ) {\n\n var matches = trackRe.exec( trackName );\n\n if ( ! matches ) {\n\n throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );\n\n }\n\n var results = {\n // directoryName: matches[ 1 ], // (tschw) currently unused\n nodeName: matches[ 2 ],\n objectName: matches[ 3 ],\n objectIndex: matches[ 4 ],\n propertyName: matches[ 5 ], // required\n propertyIndex: matches[ 6 ]\n };\n\n var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );\n\n if ( lastDot !== undefined && lastDot !== - 1 ) {\n\n var objectName = results.nodeName.substring( lastDot + 1 );\n\n // Object names must be checked against a whitelist. Otherwise, there\n // is no way to parse 'foo.bar.baz': 'baz' must be a property, but\n // 'bar' could be the objectName, or part of a nodeName (which can\n // include '.' characters).\n if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) {\n\n results.nodeName = results.nodeName.substring( 0, lastDot );\n results.objectName = objectName;\n\n }\n\n }\n\n if ( results.propertyName === null || results.propertyName.length === 0 ) {\n\n throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );\n\n }\n\n return results;\n\n };\n\n }(),\n\n findNode: function ( root, nodeName ) {\n\n if ( ! nodeName || nodeName === \"\" || nodeName === \"root\" || nodeName === \".\" || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {\n\n return root;\n\n }\n\n // search into skeleton bones.\n if ( root.skeleton ) {\n\n var bone = root.skeleton.getBoneByName( nodeName );\n\n if ( bone !== undefined ) {\n\n return bone;\n\n }\n\n }\n\n // search into node subtree.\n if ( root.children ) {\n\n var searchNodeSubtree = function ( children ) {\n\n for ( var i = 0; i < children.length; i ++ ) {\n\n var childNode = children[ i ];\n\n if ( childNode.name === nodeName || childNode.uuid === nodeName ) {\n\n return childNode;\n\n }\n\n var result = searchNodeSubtree( childNode.children );\n\n if ( result ) return result;\n\n }\n\n return null;\n\n };\n\n var subTreeNode = searchNodeSubtree( root.children );\n\n if ( subTreeNode ) {\n\n return subTreeNode;\n\n }\n\n }\n\n return null;\n\n }\n\n } );\n\n Object.assign( PropertyBinding.prototype, { // prototype, continued\n\n // these are used to \"bind\" a nonexistent property\n _getValue_unavailable: function () {},\n _setValue_unavailable: function () {},\n\n BindingType: {\n Direct: 0,\n EntireArray: 1,\n ArrayElement: 2,\n HasFromToArray: 3\n },\n\n Versioning: {\n None: 0,\n NeedsUpdate: 1,\n MatrixWorldNeedsUpdate: 2\n },\n\n GetterByBindingType: [\n\n function getValue_direct( buffer, offset ) {\n\n buffer[ offset ] = this.node[ this.propertyName ];\n\n },\n\n function getValue_array( buffer, offset ) {\n\n var source = this.resolvedProperty;\n\n for ( var i = 0, n = source.length; i !== n; ++ i ) {\n\n buffer[ offset ++ ] = source[ i ];\n\n }\n\n },\n\n function getValue_arrayElement( buffer, offset ) {\n\n buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];\n\n },\n\n function getValue_toArray( buffer, offset ) {\n\n this.resolvedProperty.toArray( buffer, offset );\n\n }\n\n ],\n\n SetterByBindingTypeAndVersioning: [\n\n [\n // Direct\n\n function setValue_direct( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n\n },\n\n function setValue_direct_setNeedsUpdate( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // EntireArray\n\n function setValue_array( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n },\n\n function setValue_array_setNeedsUpdate( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // ArrayElement\n\n function setValue_arrayElement( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n\n },\n\n function setValue_arrayElement_setNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // HasToFromArray\n\n function setValue_fromArray( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n\n },\n\n function setValue_fromArray_setNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ]\n\n ],\n\n getValue: function getValue_unbound( targetArray, offset ) {\n\n this.bind();\n this.getValue( targetArray, offset );\n\n // Note: This class uses a State pattern on a per-method basis:\n // 'bind' sets 'this.getValue' / 'setValue' and shadows the\n // prototype version of these methods with one that represents\n // the bound state. When the property is not found, the methods\n // become no-ops.\n\n },\n\n setValue: function getValue_unbound( sourceArray, offset ) {\n\n this.bind();\n this.setValue( sourceArray, offset );\n\n },\n\n // create getter / setter pair for a property in the scene graph\n bind: function () {\n\n var targetObject = this.node,\n parsedPath = this.parsedPath,\n\n objectName = parsedPath.objectName,\n propertyName = parsedPath.propertyName,\n propertyIndex = parsedPath.propertyIndex;\n\n if ( ! targetObject ) {\n\n targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode;\n\n this.node = targetObject;\n\n }\n\n // set fail state so we can just 'return' on error\n this.getValue = this._getValue_unavailable;\n this.setValue = this._setValue_unavailable;\n\n // ensure there is a value node\n if ( ! targetObject ) {\n\n console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\\'t found.' );\n return;\n\n }\n\n if ( objectName ) {\n\n var objectIndex = parsedPath.objectIndex;\n\n // special cases were we need to reach deeper into the hierarchy to get the face materials....\n switch ( objectName ) {\n\n case 'materials':\n\n if ( ! targetObject.material ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );\n return;\n\n }\n\n if ( ! targetObject.material.materials ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this );\n return;\n\n }\n\n targetObject = targetObject.material.materials;\n\n break;\n\n case 'bones':\n\n if ( ! targetObject.skeleton ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this );\n return;\n\n }\n\n // potential future optimization: skip this if propertyIndex is already an integer\n // and convert the integer string to a true integer.\n\n targetObject = targetObject.skeleton.bones;\n\n // support resolving morphTarget names into indices.\n for ( var i = 0; i < targetObject.length; i ++ ) {\n\n if ( targetObject[ i ].name === objectIndex ) {\n\n objectIndex = i;\n break;\n\n }\n\n }\n\n break;\n\n default:\n\n if ( targetObject[ objectName ] === undefined ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this );\n return;\n\n }\n\n targetObject = targetObject[ objectName ];\n\n }\n\n\n if ( objectIndex !== undefined ) {\n\n if ( targetObject[ objectIndex ] === undefined ) {\n\n console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject );\n return;\n\n }\n\n targetObject = targetObject[ objectIndex ];\n\n }\n\n }\n\n // resolve property\n var nodeProperty = targetObject[ propertyName ];\n\n if ( nodeProperty === undefined ) {\n\n var nodeName = parsedPath.nodeName;\n\n console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName +\n '.' + propertyName + ' but it wasn\\'t found.', targetObject );\n return;\n\n }\n\n // determine versioning scheme\n var versioning = this.Versioning.None;\n\n if ( targetObject.needsUpdate !== undefined ) { // material\n\n versioning = this.Versioning.NeedsUpdate;\n this.targetObject = targetObject;\n\n } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform\n\n versioning = this.Versioning.MatrixWorldNeedsUpdate;\n this.targetObject = targetObject;\n\n }\n\n // determine how the property gets bound\n var bindingType = this.BindingType.Direct;\n\n if ( propertyIndex !== undefined ) {\n\n // access a sub element of the property array (only primitives are supported right now)\n\n if ( propertyName === \"morphTargetInfluences\" ) {\n\n // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.\n\n // support resolving morphTarget names into indices.\n if ( ! targetObject.geometry ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this );\n return;\n\n }\n\n if ( targetObject.geometry.isBufferGeometry ) {\n\n if ( ! targetObject.geometry.morphAttributes ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this );\n return;\n\n }\n\n for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) {\n\n if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) {\n\n propertyIndex = i;\n break;\n\n }\n\n }\n\n\n } else {\n\n if ( ! targetObject.geometry.morphTargets ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.', this );\n return;\n\n }\n\n for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {\n\n if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {\n\n propertyIndex = i;\n break;\n\n }\n\n }\n\n }\n\n }\n\n bindingType = this.BindingType.ArrayElement;\n\n this.resolvedProperty = nodeProperty;\n this.propertyIndex = propertyIndex;\n\n } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {\n\n // must use copy for Object3D.Euler/Quaternion\n\n bindingType = this.BindingType.HasFromToArray;\n\n this.resolvedProperty = nodeProperty;\n\n } else if ( Array.isArray( nodeProperty ) ) {\n\n bindingType = this.BindingType.EntireArray;\n\n this.resolvedProperty = nodeProperty;\n\n } else {\n\n this.propertyName = propertyName;\n\n }\n\n // select getter / setter\n this.getValue = this.GetterByBindingType[ bindingType ];\n this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];\n\n },\n\n unbind: function () {\n\n this.node = null;\n\n // back to the prototype version of getValue / setValue\n // note: avoiding to mutate the shape of 'this' via 'delete'\n this.getValue = this._getValue_unbound;\n this.setValue = this._setValue_unbound;\n\n }\n\n } );\n\n //!\\ DECLARE ALIAS AFTER assign prototype !\n Object.assign( PropertyBinding.prototype, {\n\n // initial state of these methods that calls 'bind'\n _getValue_unbound: PropertyBinding.prototype.getValue,\n _setValue_unbound: PropertyBinding.prototype.setValue,\n\n } );\n\n /**\n *\n * A group of objects that receives a shared animation state.\n *\n * Usage:\n *\n * - Add objects you would otherwise pass as 'root' to the\n * constructor or the .clipAction method of AnimationMixer.\n *\n * - Instead pass this object as 'root'.\n *\n * - You can also add and remove objects later when the mixer\n * is running.\n *\n * Note:\n *\n * Objects of this class appear as one object to the mixer,\n * so cache control of the individual objects must be done\n * on the group.\n *\n * Limitation:\n *\n * - The animated properties must be compatible among the\n * all objects in the group.\n *\n * - A single property can either be controlled through a\n * target group or directly, but not both.\n *\n * @author tschw\n */\n\n function AnimationObjectGroup() {\n\n this.uuid = _Math.generateUUID();\n\n // cached objects followed by the active ones\n this._objects = Array.prototype.slice.call( arguments );\n\n this.nCachedObjects_ = 0; // threshold\n // note: read by PropertyBinding.Composite\n\n var indices = {};\n this._indicesByUUID = indices; // for bookkeeping\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n indices[ arguments[ i ].uuid ] = i;\n\n }\n\n this._paths = []; // inside: string\n this._parsedPaths = []; // inside: { we don't care, here }\n this._bindings = []; // inside: Array< PropertyBinding >\n this._bindingsIndicesByPath = {}; // inside: indices in these arrays\n\n var scope = this;\n\n this.stats = {\n\n objects: {\n get total() {\n\n return scope._objects.length;\n\n },\n get inUse() {\n\n return this.total - scope.nCachedObjects_;\n\n }\n },\n get bindingsPerObject() {\n\n return scope._bindings.length;\n\n }\n\n };\n\n }\n\n Object.assign( AnimationObjectGroup.prototype, {\n\n isAnimationObjectGroup: true,\n\n add: function () {\n\n var objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n paths = this._paths,\n parsedPaths = this._parsedPaths,\n bindings = this._bindings,\n nBindings = bindings.length,\n knownObject = undefined;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index === undefined ) {\n\n // unknown object -> add it to the ACTIVE region\n\n index = nObjects ++;\n indicesByUUID[ uuid ] = index;\n objects.push( object );\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) );\n\n }\n\n } else if ( index < nCachedObjects ) {\n\n knownObject = objects[ index ];\n\n // move existing object to the ACTIVE region\n\n var firstActiveIndex = -- nCachedObjects,\n lastCachedObject = objects[ firstActiveIndex ];\n\n indicesByUUID[ lastCachedObject.uuid ] = index;\n objects[ index ] = lastCachedObject;\n\n indicesByUUID[ uuid ] = firstActiveIndex;\n objects[ firstActiveIndex ] = object;\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n lastCached = bindingsForPath[ firstActiveIndex ],\n binding = bindingsForPath[ index ];\n\n bindingsForPath[ index ] = lastCached;\n\n if ( binding === undefined ) {\n\n // since we do not bother to create new bindings\n // for objects that are cached, the binding may\n // or may not exist\n\n binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] );\n\n }\n\n bindingsForPath[ firstActiveIndex ] = binding;\n\n }\n\n } else if ( objects[ index ] !== knownObject ) {\n\n console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' +\n 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' );\n\n } // else the object is already where we want it to be\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n remove: function () {\n\n var objects = this._objects,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n bindings = this._bindings,\n nBindings = bindings.length;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index !== undefined && index >= nCachedObjects ) {\n\n // move existing object into the CACHED region\n\n var lastCachedIndex = nCachedObjects ++,\n firstActiveObject = objects[ lastCachedIndex ];\n\n indicesByUUID[ firstActiveObject.uuid ] = index;\n objects[ index ] = firstActiveObject;\n\n indicesByUUID[ uuid ] = lastCachedIndex;\n objects[ lastCachedIndex ] = object;\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n firstActive = bindingsForPath[ lastCachedIndex ],\n binding = bindingsForPath[ index ];\n\n bindingsForPath[ index ] = firstActive;\n bindingsForPath[ lastCachedIndex ] = binding;\n\n }\n\n }\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n // remove & forget\n uncache: function () {\n\n var objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n bindings = this._bindings,\n nBindings = bindings.length;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index !== undefined ) {\n\n delete indicesByUUID[ uuid ];\n\n if ( index < nCachedObjects ) {\n\n // object is cached, shrink the CACHED region\n\n var firstActiveIndex = -- nCachedObjects,\n lastCachedObject = objects[ firstActiveIndex ],\n lastIndex = -- nObjects,\n lastObject = objects[ lastIndex ];\n\n // last cached object takes this object's place\n indicesByUUID[ lastCachedObject.uuid ] = index;\n objects[ index ] = lastCachedObject;\n\n // last object goes to the activated slot and pop\n indicesByUUID[ lastObject.uuid ] = firstActiveIndex;\n objects[ firstActiveIndex ] = lastObject;\n objects.pop();\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n lastCached = bindingsForPath[ firstActiveIndex ],\n last = bindingsForPath[ lastIndex ];\n\n bindingsForPath[ index ] = lastCached;\n bindingsForPath[ firstActiveIndex ] = last;\n bindingsForPath.pop();\n\n }\n\n } else {\n\n // object is active, just swap with the last and pop\n\n var lastIndex = -- nObjects,\n lastObject = objects[ lastIndex ];\n\n indicesByUUID[ lastObject.uuid ] = index;\n objects[ index ] = lastObject;\n objects.pop();\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ];\n\n bindingsForPath[ index ] = bindingsForPath[ lastIndex ];\n bindingsForPath.pop();\n\n }\n\n } // cached or active\n\n } // if object is known\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n // Internal interface used by befriended PropertyBinding.Composite:\n\n subscribe_: function ( path, parsedPath ) {\n\n // returns an array of bindings for the given path that is changed\n // according to the contained objects in the group\n\n var indicesByPath = this._bindingsIndicesByPath,\n index = indicesByPath[ path ],\n bindings = this._bindings;\n\n if ( index !== undefined ) return bindings[ index ];\n\n var paths = this._paths,\n parsedPaths = this._parsedPaths,\n objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n bindingsForPath = new Array( nObjects );\n\n index = bindings.length;\n\n indicesByPath[ path ] = index;\n\n paths.push( path );\n parsedPaths.push( parsedPath );\n bindings.push( bindingsForPath );\n\n for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {\n\n var object = objects[ i ];\n bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );\n\n }\n\n return bindingsForPath;\n\n },\n\n unsubscribe_: function ( path ) {\n\n // tells the group to forget about a property path and no longer\n // update the array previously obtained with 'subscribe_'\n\n var indicesByPath = this._bindingsIndicesByPath,\n index = indicesByPath[ path ];\n\n if ( index !== undefined ) {\n\n var paths = this._paths,\n parsedPaths = this._parsedPaths,\n bindings = this._bindings,\n lastBindingsIndex = bindings.length - 1,\n lastBindings = bindings[ lastBindingsIndex ],\n lastBindingsPath = path[ lastBindingsIndex ];\n\n indicesByPath[ lastBindingsPath ] = index;\n\n bindings[ index ] = lastBindings;\n bindings.pop();\n\n parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ];\n parsedPaths.pop();\n\n paths[ index ] = paths[ lastBindingsIndex ];\n paths.pop();\n\n }\n\n }\n\n } );\n\n /**\n *\n * Action provided by AnimationMixer for scheduling clip playback on specific\n * objects.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n *\n */\n\n function AnimationAction( mixer, clip, localRoot ) {\n\n this._mixer = mixer;\n this._clip = clip;\n this._localRoot = localRoot || null;\n\n var tracks = clip.tracks,\n nTracks = tracks.length,\n interpolants = new Array( nTracks );\n\n var interpolantSettings = {\n endingStart: ZeroCurvatureEnding,\n endingEnd: ZeroCurvatureEnding\n };\n\n for ( var i = 0; i !== nTracks; ++ i ) {\n\n var interpolant = tracks[ i ].createInterpolant( null );\n interpolants[ i ] = interpolant;\n interpolant.settings = interpolantSettings;\n\n }\n\n this._interpolantSettings = interpolantSettings;\n\n this._interpolants = interpolants; // bound by the mixer\n\n // inside: PropertyMixer (managed by the mixer)\n this._propertyBindings = new Array( nTracks );\n\n this._cacheIndex = null; // for the memory manager\n this._byClipCacheIndex = null; // for the memory manager\n\n this._timeScaleInterpolant = null;\n this._weightInterpolant = null;\n\n this.loop = LoopRepeat;\n this._loopCount = - 1;\n\n // global mixer time when the action is to be started\n // it's set back to 'null' upon start of the action\n this._startTime = null;\n\n // scaled local time of the action\n // gets clamped or wrapped to 0..clip.duration according to loop\n this.time = 0;\n\n this.timeScale = 1;\n this._effectiveTimeScale = 1;\n\n this.weight = 1;\n this._effectiveWeight = 1;\n\n this.repetitions = Infinity; // no. of repetitions when looping\n\n this.paused = false; // true -> zero effective time scale\n this.enabled = true; // false -> zero effective weight\n\n this.clampWhenFinished = false; // keep feeding the last frame?\n\n this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate\n this.zeroSlopeAtEnd = true; // clips for start, loop and end\n\n }\n\n Object.assign( AnimationAction.prototype, {\n\n // State & Scheduling\n\n play: function () {\n\n this._mixer._activateAction( this );\n\n return this;\n\n },\n\n stop: function () {\n\n this._mixer._deactivateAction( this );\n\n return this.reset();\n\n },\n\n reset: function () {\n\n this.paused = false;\n this.enabled = true;\n\n this.time = 0; // restart clip\n this._loopCount = - 1; // forget previous loops\n this._startTime = null; // forget scheduling\n\n return this.stopFading().stopWarping();\n\n },\n\n isRunning: function () {\n\n return this.enabled && ! this.paused && this.timeScale !== 0 &&\n this._startTime === null && this._mixer._isActiveAction( this );\n\n },\n\n // return true when play has been called\n isScheduled: function () {\n\n return this._mixer._isActiveAction( this );\n\n },\n\n startAt: function ( time ) {\n\n this._startTime = time;\n\n return this;\n\n },\n\n setLoop: function ( mode, repetitions ) {\n\n this.loop = mode;\n this.repetitions = repetitions;\n\n return this;\n\n },\n\n // Weight\n\n // set the weight stopping any scheduled fading\n // although .enabled = false yields an effective weight of zero, this\n // method does *not* change .enabled, because it would be confusing\n setEffectiveWeight: function ( weight ) {\n\n this.weight = weight;\n\n // note: same logic as when updated at runtime\n this._effectiveWeight = this.enabled ? weight : 0;\n\n return this.stopFading();\n\n },\n\n // return the weight considering fading and .enabled\n getEffectiveWeight: function () {\n\n return this._effectiveWeight;\n\n },\n\n fadeIn: function ( duration ) {\n\n return this._scheduleFading( duration, 0, 1 );\n\n },\n\n fadeOut: function ( duration ) {\n\n return this._scheduleFading( duration, 1, 0 );\n\n },\n\n crossFadeFrom: function ( fadeOutAction, duration, warp ) {\n\n fadeOutAction.fadeOut( duration );\n this.fadeIn( duration );\n\n if ( warp ) {\n\n var fadeInDuration = this._clip.duration,\n fadeOutDuration = fadeOutAction._clip.duration,\n\n startEndRatio = fadeOutDuration / fadeInDuration,\n endStartRatio = fadeInDuration / fadeOutDuration;\n\n fadeOutAction.warp( 1.0, startEndRatio, duration );\n this.warp( endStartRatio, 1.0, duration );\n\n }\n\n return this;\n\n },\n\n crossFadeTo: function ( fadeInAction, duration, warp ) {\n\n return fadeInAction.crossFadeFrom( this, duration, warp );\n\n },\n\n stopFading: function () {\n\n var weightInterpolant = this._weightInterpolant;\n\n if ( weightInterpolant !== null ) {\n\n this._weightInterpolant = null;\n this._mixer._takeBackControlInterpolant( weightInterpolant );\n\n }\n\n return this;\n\n },\n\n // Time Scale Control\n\n // set the time scale stopping any scheduled warping\n // although .paused = true yields an effective time scale of zero, this\n // method does *not* change .paused, because it would be confusing\n setEffectiveTimeScale: function ( timeScale ) {\n\n this.timeScale = timeScale;\n this._effectiveTimeScale = this.paused ? 0 : timeScale;\n\n return this.stopWarping();\n\n },\n\n // return the time scale considering warping and .paused\n getEffectiveTimeScale: function () {\n\n return this._effectiveTimeScale;\n\n },\n\n setDuration: function ( duration ) {\n\n this.timeScale = this._clip.duration / duration;\n\n return this.stopWarping();\n\n },\n\n syncWith: function ( action ) {\n\n this.time = action.time;\n this.timeScale = action.timeScale;\n\n return this.stopWarping();\n\n },\n\n halt: function ( duration ) {\n\n return this.warp( this._effectiveTimeScale, 0, duration );\n\n },\n\n warp: function ( startTimeScale, endTimeScale, duration ) {\n\n var mixer = this._mixer, now = mixer.time,\n interpolant = this._timeScaleInterpolant,\n\n timeScale = this.timeScale;\n\n if ( interpolant === null ) {\n\n interpolant = mixer._lendControlInterpolant();\n this._timeScaleInterpolant = interpolant;\n\n }\n\n var times = interpolant.parameterPositions,\n values = interpolant.sampleValues;\n\n times[ 0 ] = now;\n times[ 1 ] = now + duration;\n\n values[ 0 ] = startTimeScale / timeScale;\n values[ 1 ] = endTimeScale / timeScale;\n\n return this;\n\n },\n\n stopWarping: function () {\n\n var timeScaleInterpolant = this._timeScaleInterpolant;\n\n if ( timeScaleInterpolant !== null ) {\n\n this._timeScaleInterpolant = null;\n this._mixer._takeBackControlInterpolant( timeScaleInterpolant );\n\n }\n\n return this;\n\n },\n\n // Object Accessors\n\n getMixer: function () {\n\n return this._mixer;\n\n },\n\n getClip: function () {\n\n return this._clip;\n\n },\n\n getRoot: function () {\n\n return this._localRoot || this._mixer._root;\n\n },\n\n // Interna\n\n _update: function ( time, deltaTime, timeDirection, accuIndex ) {\n\n // called by the mixer\n\n if ( ! this.enabled ) {\n\n // call ._updateWeight() to update ._effectiveWeight\n\n this._updateWeight( time );\n return;\n\n }\n\n var startTime = this._startTime;\n\n if ( startTime !== null ) {\n\n // check for scheduled start of action\n\n var timeRunning = ( time - startTime ) * timeDirection;\n if ( timeRunning < 0 || timeDirection === 0 ) {\n\n return; // yet to come / don't decide when delta = 0\n\n }\n\n // start\n\n this._startTime = null; // unschedule\n deltaTime = timeDirection * timeRunning;\n\n }\n\n // apply time scale and advance time\n\n deltaTime *= this._updateTimeScale( time );\n var clipTime = this._updateTime( deltaTime );\n\n // note: _updateTime may disable the action resulting in\n // an effective weight of 0\n\n var weight = this._updateWeight( time );\n\n if ( weight > 0 ) {\n\n var interpolants = this._interpolants;\n var propertyMixers = this._propertyBindings;\n\n for ( var j = 0, m = interpolants.length; j !== m; ++ j ) {\n\n interpolants[ j ].evaluate( clipTime );\n propertyMixers[ j ].accumulate( accuIndex, weight );\n\n }\n\n }\n\n },\n\n _updateWeight: function ( time ) {\n\n var weight = 0;\n\n if ( this.enabled ) {\n\n weight = this.weight;\n var interpolant = this._weightInterpolant;\n\n if ( interpolant !== null ) {\n\n var interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n weight *= interpolantValue;\n\n if ( time > interpolant.parameterPositions[ 1 ] ) {\n\n this.stopFading();\n\n if ( interpolantValue === 0 ) {\n\n // faded out, disable\n this.enabled = false;\n\n }\n\n }\n\n }\n\n }\n\n this._effectiveWeight = weight;\n return weight;\n\n },\n\n _updateTimeScale: function ( time ) {\n\n var timeScale = 0;\n\n if ( ! this.paused ) {\n\n timeScale = this.timeScale;\n\n var interpolant = this._timeScaleInterpolant;\n\n if ( interpolant !== null ) {\n\n var interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n timeScale *= interpolantValue;\n\n if ( time > interpolant.parameterPositions[ 1 ] ) {\n\n this.stopWarping();\n\n if ( timeScale === 0 ) {\n\n // motion has halted, pause\n this.paused = true;\n\n } else {\n\n // warp done - apply final time scale\n this.timeScale = timeScale;\n\n }\n\n }\n\n }\n\n }\n\n this._effectiveTimeScale = timeScale;\n return timeScale;\n\n },\n\n _updateTime: function ( deltaTime ) {\n\n var time = this.time + deltaTime;\n\n if ( deltaTime === 0 ) return time;\n\n var duration = this._clip.duration,\n\n loop = this.loop,\n loopCount = this._loopCount;\n\n if ( loop === LoopOnce ) {\n\n if ( loopCount === - 1 ) {\n\n // just started\n\n this._loopCount = 0;\n this._setEndings( true, true, false );\n\n }\n\n handle_stop: {\n\n if ( time >= duration ) {\n\n time = duration;\n\n } else if ( time < 0 ) {\n\n time = 0;\n\n } else break handle_stop;\n\n if ( this.clampWhenFinished ) this.paused = true;\n else this.enabled = false;\n\n this._mixer.dispatchEvent( {\n type: 'finished', action: this,\n direction: deltaTime < 0 ? - 1 : 1\n } );\n\n }\n\n } else { // repetitive Repeat or PingPong\n\n var pingPong = ( loop === LoopPingPong );\n\n if ( loopCount === - 1 ) {\n\n // just started\n\n if ( deltaTime >= 0 ) {\n\n loopCount = 0;\n\n this._setEndings( true, this.repetitions === 0, pingPong );\n\n } else {\n\n // when looping in reverse direction, the initial\n // transition through zero counts as a repetition,\n // so leave loopCount at -1\n\n this._setEndings( this.repetitions === 0, true, pingPong );\n\n }\n\n }\n\n if ( time >= duration || time < 0 ) {\n\n // wrap around\n\n var loopDelta = Math.floor( time / duration ); // signed\n time -= duration * loopDelta;\n\n loopCount += Math.abs( loopDelta );\n\n var pending = this.repetitions - loopCount;\n\n if ( pending <= 0 ) {\n\n // have to stop (switch state, clamp time, fire event)\n\n if ( this.clampWhenFinished ) this.paused = true;\n else this.enabled = false;\n\n time = deltaTime > 0 ? duration : 0;\n\n this._mixer.dispatchEvent( {\n type: 'finished', action: this,\n direction: deltaTime > 0 ? 1 : - 1\n } );\n\n } else {\n\n // keep running\n\n if ( pending === 1 ) {\n\n // entering the last round\n\n var atStart = deltaTime < 0;\n this._setEndings( atStart, ! atStart, pingPong );\n\n } else {\n\n this._setEndings( false, false, pingPong );\n\n }\n\n this._loopCount = loopCount;\n\n this._mixer.dispatchEvent( {\n type: 'loop', action: this, loopDelta: loopDelta\n } );\n\n }\n\n }\n\n if ( pingPong && ( loopCount & 1 ) === 1 ) {\n\n // invert time for the \"pong round\"\n\n this.time = time;\n return duration - time;\n\n }\n\n }\n\n this.time = time;\n return time;\n\n },\n\n _setEndings: function ( atStart, atEnd, pingPong ) {\n\n var settings = this._interpolantSettings;\n\n if ( pingPong ) {\n\n settings.endingStart = ZeroSlopeEnding;\n settings.endingEnd = ZeroSlopeEnding;\n\n } else {\n\n // assuming for LoopOnce atStart == atEnd == true\n\n if ( atStart ) {\n\n settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n } else {\n\n settings.endingStart = WrapAroundEnding;\n\n }\n\n if ( atEnd ) {\n\n settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n } else {\n\n settings.endingEnd = WrapAroundEnding;\n\n }\n\n }\n\n },\n\n _scheduleFading: function ( duration, weightNow, weightThen ) {\n\n var mixer = this._mixer, now = mixer.time,\n interpolant = this._weightInterpolant;\n\n if ( interpolant === null ) {\n\n interpolant = mixer._lendControlInterpolant();\n this._weightInterpolant = interpolant;\n\n }\n\n var times = interpolant.parameterPositions,\n values = interpolant.sampleValues;\n\n times[ 0 ] = now; values[ 0 ] = weightNow;\n times[ 1 ] = now + duration; values[ 1 ] = weightThen;\n\n return this;\n\n }\n\n } );\n\n /**\n *\n * Player for AnimationClips.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function AnimationMixer( root ) {\n\n this._root = root;\n this._initMemoryManager();\n this._accuIndex = 0;\n\n this.time = 0;\n\n this.timeScale = 1.0;\n\n }\n\n AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: AnimationMixer,\n\n _bindAction: function ( action, prototypeAction ) {\n\n var root = action._localRoot || this._root,\n tracks = action._clip.tracks,\n nTracks = tracks.length,\n bindings = action._propertyBindings,\n interpolants = action._interpolants,\n rootUuid = root.uuid,\n bindingsByRoot = this._bindingsByRootAndName,\n bindingsByName = bindingsByRoot[ rootUuid ];\n\n if ( bindingsByName === undefined ) {\n\n bindingsByName = {};\n bindingsByRoot[ rootUuid ] = bindingsByName;\n\n }\n\n for ( var i = 0; i !== nTracks; ++ i ) {\n\n var track = tracks[ i ],\n trackName = track.name,\n binding = bindingsByName[ trackName ];\n\n if ( binding !== undefined ) {\n\n bindings[ i ] = binding;\n\n } else {\n\n binding = bindings[ i ];\n\n if ( binding !== undefined ) {\n\n // existing binding, make sure the cache knows\n\n if ( binding._cacheIndex === null ) {\n\n ++ binding.referenceCount;\n this._addInactiveBinding( binding, rootUuid, trackName );\n\n }\n\n continue;\n\n }\n\n var path = prototypeAction && prototypeAction.\n _propertyBindings[ i ].binding.parsedPath;\n\n binding = new PropertyMixer(\n PropertyBinding.create( root, trackName, path ),\n track.ValueTypeName, track.getValueSize() );\n\n ++ binding.referenceCount;\n this._addInactiveBinding( binding, rootUuid, trackName );\n\n bindings[ i ] = binding;\n\n }\n\n interpolants[ i ].resultBuffer = binding.buffer;\n\n }\n\n },\n\n _activateAction: function ( action ) {\n\n if ( ! this._isActiveAction( action ) ) {\n\n if ( action._cacheIndex === null ) {\n\n // this action has been forgotten by the cache, but the user\n // appears to be still using it -> rebind\n\n var rootUuid = ( action._localRoot || this._root ).uuid,\n clipUuid = action._clip.uuid,\n actionsForClip = this._actionsByClip[ clipUuid ];\n\n this._bindAction( action,\n actionsForClip && actionsForClip.knownActions[ 0 ] );\n\n this._addInactiveAction( action, clipUuid, rootUuid );\n\n }\n\n var bindings = action._propertyBindings;\n\n // increment reference counts / sort out state\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( binding.useCount ++ === 0 ) {\n\n this._lendBinding( binding );\n binding.saveOriginalState();\n\n }\n\n }\n\n this._lendAction( action );\n\n }\n\n },\n\n _deactivateAction: function ( action ) {\n\n if ( this._isActiveAction( action ) ) {\n\n var bindings = action._propertyBindings;\n\n // decrement reference counts / sort out state\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( -- binding.useCount === 0 ) {\n\n binding.restoreOriginalState();\n this._takeBackBinding( binding );\n\n }\n\n }\n\n this._takeBackAction( action );\n\n }\n\n },\n\n // Memory manager\n\n _initMemoryManager: function () {\n\n this._actions = []; // 'nActiveActions' followed by inactive ones\n this._nActiveActions = 0;\n\n this._actionsByClip = {};\n // inside:\n // {\n // knownActions: Array< AnimationAction > - used as prototypes\n // actionByRoot: AnimationAction - lookup\n // }\n\n\n this._bindings = []; // 'nActiveBindings' followed by inactive ones\n this._nActiveBindings = 0;\n\n this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer >\n\n\n this._controlInterpolants = []; // same game as above\n this._nActiveControlInterpolants = 0;\n\n var scope = this;\n\n this.stats = {\n\n actions: {\n get total() {\n\n return scope._actions.length;\n\n },\n get inUse() {\n\n return scope._nActiveActions;\n\n }\n },\n bindings: {\n get total() {\n\n return scope._bindings.length;\n\n },\n get inUse() {\n\n return scope._nActiveBindings;\n\n }\n },\n controlInterpolants: {\n get total() {\n\n return scope._controlInterpolants.length;\n\n },\n get inUse() {\n\n return scope._nActiveControlInterpolants;\n\n }\n }\n\n };\n\n },\n\n // Memory management for AnimationAction objects\n\n _isActiveAction: function ( action ) {\n\n var index = action._cacheIndex;\n return index !== null && index < this._nActiveActions;\n\n },\n\n _addInactiveAction: function ( action, clipUuid, rootUuid ) {\n\n var actions = this._actions,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ];\n\n if ( actionsForClip === undefined ) {\n\n actionsForClip = {\n\n knownActions: [ action ],\n actionByRoot: {}\n\n };\n\n action._byClipCacheIndex = 0;\n\n actionsByClip[ clipUuid ] = actionsForClip;\n\n } else {\n\n var knownActions = actionsForClip.knownActions;\n\n action._byClipCacheIndex = knownActions.length;\n knownActions.push( action );\n\n }\n\n action._cacheIndex = actions.length;\n actions.push( action );\n\n actionsForClip.actionByRoot[ rootUuid ] = action;\n\n },\n\n _removeInactiveAction: function ( action ) {\n\n var actions = this._actions,\n lastInactiveAction = actions[ actions.length - 1 ],\n cacheIndex = action._cacheIndex;\n\n lastInactiveAction._cacheIndex = cacheIndex;\n actions[ cacheIndex ] = lastInactiveAction;\n actions.pop();\n\n action._cacheIndex = null;\n\n\n var clipUuid = action._clip.uuid,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ],\n knownActionsForClip = actionsForClip.knownActions,\n\n lastKnownAction =\n knownActionsForClip[ knownActionsForClip.length - 1 ],\n\n byClipCacheIndex = action._byClipCacheIndex;\n\n lastKnownAction._byClipCacheIndex = byClipCacheIndex;\n knownActionsForClip[ byClipCacheIndex ] = lastKnownAction;\n knownActionsForClip.pop();\n\n action._byClipCacheIndex = null;\n\n\n var actionByRoot = actionsForClip.actionByRoot,\n rootUuid = ( action._localRoot || this._root ).uuid;\n\n delete actionByRoot[ rootUuid ];\n\n if ( knownActionsForClip.length === 0 ) {\n\n delete actionsByClip[ clipUuid ];\n\n }\n\n this._removeInactiveBindingsForAction( action );\n\n },\n\n _removeInactiveBindingsForAction: function ( action ) {\n\n var bindings = action._propertyBindings;\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( -- binding.referenceCount === 0 ) {\n\n this._removeInactiveBinding( binding );\n\n }\n\n }\n\n },\n\n _lendAction: function ( action ) {\n\n // [ active actions | inactive actions ]\n // [ active actions >| inactive actions ]\n // s a\n // <-swap->\n // a s\n\n var actions = this._actions,\n prevIndex = action._cacheIndex,\n\n lastActiveIndex = this._nActiveActions ++,\n\n firstInactiveAction = actions[ lastActiveIndex ];\n\n action._cacheIndex = lastActiveIndex;\n actions[ lastActiveIndex ] = action;\n\n firstInactiveAction._cacheIndex = prevIndex;\n actions[ prevIndex ] = firstInactiveAction;\n\n },\n\n _takeBackAction: function ( action ) {\n\n // [ active actions | inactive actions ]\n // [ active actions |< inactive actions ]\n // a s\n // <-swap->\n // s a\n\n var actions = this._actions,\n prevIndex = action._cacheIndex,\n\n firstInactiveIndex = -- this._nActiveActions,\n\n lastActiveAction = actions[ firstInactiveIndex ];\n\n action._cacheIndex = firstInactiveIndex;\n actions[ firstInactiveIndex ] = action;\n\n lastActiveAction._cacheIndex = prevIndex;\n actions[ prevIndex ] = lastActiveAction;\n\n },\n\n // Memory management for PropertyMixer objects\n\n _addInactiveBinding: function ( binding, rootUuid, trackName ) {\n\n var bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ],\n\n bindings = this._bindings;\n\n if ( bindingByName === undefined ) {\n\n bindingByName = {};\n bindingsByRoot[ rootUuid ] = bindingByName;\n\n }\n\n bindingByName[ trackName ] = binding;\n\n binding._cacheIndex = bindings.length;\n bindings.push( binding );\n\n },\n\n _removeInactiveBinding: function ( binding ) {\n\n var bindings = this._bindings,\n propBinding = binding.binding,\n rootUuid = propBinding.rootNode.uuid,\n trackName = propBinding.path,\n bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ],\n\n lastInactiveBinding = bindings[ bindings.length - 1 ],\n cacheIndex = binding._cacheIndex;\n\n lastInactiveBinding._cacheIndex = cacheIndex;\n bindings[ cacheIndex ] = lastInactiveBinding;\n bindings.pop();\n\n delete bindingByName[ trackName ];\n\n remove_empty_map: {\n\n for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars\n\n delete bindingsByRoot[ rootUuid ];\n\n }\n\n },\n\n _lendBinding: function ( binding ) {\n\n var bindings = this._bindings,\n prevIndex = binding._cacheIndex,\n\n lastActiveIndex = this._nActiveBindings ++,\n\n firstInactiveBinding = bindings[ lastActiveIndex ];\n\n binding._cacheIndex = lastActiveIndex;\n bindings[ lastActiveIndex ] = binding;\n\n firstInactiveBinding._cacheIndex = prevIndex;\n bindings[ prevIndex ] = firstInactiveBinding;\n\n },\n\n _takeBackBinding: function ( binding ) {\n\n var bindings = this._bindings,\n prevIndex = binding._cacheIndex,\n\n firstInactiveIndex = -- this._nActiveBindings,\n\n lastActiveBinding = bindings[ firstInactiveIndex ];\n\n binding._cacheIndex = firstInactiveIndex;\n bindings[ firstInactiveIndex ] = binding;\n\n lastActiveBinding._cacheIndex = prevIndex;\n bindings[ prevIndex ] = lastActiveBinding;\n\n },\n\n\n // Memory management of Interpolants for weight and time scale\n\n _lendControlInterpolant: function () {\n\n var interpolants = this._controlInterpolants,\n lastActiveIndex = this._nActiveControlInterpolants ++,\n interpolant = interpolants[ lastActiveIndex ];\n\n if ( interpolant === undefined ) {\n\n interpolant = new LinearInterpolant(\n new Float32Array( 2 ), new Float32Array( 2 ),\n 1, this._controlInterpolantsResultBuffer );\n\n interpolant.__cacheIndex = lastActiveIndex;\n interpolants[ lastActiveIndex ] = interpolant;\n\n }\n\n return interpolant;\n\n },\n\n _takeBackControlInterpolant: function ( interpolant ) {\n\n var interpolants = this._controlInterpolants,\n prevIndex = interpolant.__cacheIndex,\n\n firstInactiveIndex = -- this._nActiveControlInterpolants,\n\n lastActiveInterpolant = interpolants[ firstInactiveIndex ];\n\n interpolant.__cacheIndex = firstInactiveIndex;\n interpolants[ firstInactiveIndex ] = interpolant;\n\n lastActiveInterpolant.__cacheIndex = prevIndex;\n interpolants[ prevIndex ] = lastActiveInterpolant;\n\n },\n\n _controlInterpolantsResultBuffer: new Float32Array( 1 ),\n\n // return an action for a clip optionally using a custom root target\n // object (this method allocates a lot of dynamic memory in case a\n // previously unknown clip/root combination is specified)\n clipAction: function ( clip, optionalRoot ) {\n\n var root = optionalRoot || this._root,\n rootUuid = root.uuid,\n\n clipObject = typeof clip === 'string' ?\n AnimationClip.findByName( root, clip ) : clip,\n\n clipUuid = clipObject !== null ? clipObject.uuid : clip,\n\n actionsForClip = this._actionsByClip[ clipUuid ],\n prototypeAction = null;\n\n if ( actionsForClip !== undefined ) {\n\n var existingAction =\n actionsForClip.actionByRoot[ rootUuid ];\n\n if ( existingAction !== undefined ) {\n\n return existingAction;\n\n }\n\n // we know the clip, so we don't have to parse all\n // the bindings again but can just copy\n prototypeAction = actionsForClip.knownActions[ 0 ];\n\n // also, take the clip from the prototype action\n if ( clipObject === null )\n clipObject = prototypeAction._clip;\n\n }\n\n // clip must be known when specified via string\n if ( clipObject === null ) return null;\n\n // allocate all resources required to run it\n var newAction = new AnimationAction( this, clipObject, optionalRoot );\n\n this._bindAction( newAction, prototypeAction );\n\n // and make the action known to the memory manager\n this._addInactiveAction( newAction, clipUuid, rootUuid );\n\n return newAction;\n\n },\n\n // get an existing action\n existingAction: function ( clip, optionalRoot ) {\n\n var root = optionalRoot || this._root,\n rootUuid = root.uuid,\n\n clipObject = typeof clip === 'string' ?\n AnimationClip.findByName( root, clip ) : clip,\n\n clipUuid = clipObject ? clipObject.uuid : clip,\n\n actionsForClip = this._actionsByClip[ clipUuid ];\n\n if ( actionsForClip !== undefined ) {\n\n return actionsForClip.actionByRoot[ rootUuid ] || null;\n\n }\n\n return null;\n\n },\n\n // deactivates all previously scheduled actions\n stopAllAction: function () {\n\n var actions = this._actions,\n nActions = this._nActiveActions,\n bindings = this._bindings,\n nBindings = this._nActiveBindings;\n\n this._nActiveActions = 0;\n this._nActiveBindings = 0;\n\n for ( var i = 0; i !== nActions; ++ i ) {\n\n actions[ i ].reset();\n\n }\n\n for ( var i = 0; i !== nBindings; ++ i ) {\n\n bindings[ i ].useCount = 0;\n\n }\n\n return this;\n\n },\n\n // advance the time and update apply the animation\n update: function ( deltaTime ) {\n\n deltaTime *= this.timeScale;\n\n var actions = this._actions,\n nActions = this._nActiveActions,\n\n time = this.time += deltaTime,\n timeDirection = Math.sign( deltaTime ),\n\n accuIndex = this._accuIndex ^= 1;\n\n // run active actions\n\n for ( var i = 0; i !== nActions; ++ i ) {\n\n var action = actions[ i ];\n\n action._update( time, deltaTime, timeDirection, accuIndex );\n\n }\n\n // update scene graph\n\n var bindings = this._bindings,\n nBindings = this._nActiveBindings;\n\n for ( var i = 0; i !== nBindings; ++ i ) {\n\n bindings[ i ].apply( accuIndex );\n\n }\n\n return this;\n\n },\n\n // return this mixer's root target object\n getRoot: function () {\n\n return this._root;\n\n },\n\n // free all resources specific to a particular clip\n uncacheClip: function ( clip ) {\n\n var actions = this._actions,\n clipUuid = clip.uuid,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ];\n\n if ( actionsForClip !== undefined ) {\n\n // note: just calling _removeInactiveAction would mess up the\n // iteration state and also require updating the state we can\n // just throw away\n\n var actionsToRemove = actionsForClip.knownActions;\n\n for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {\n\n var action = actionsToRemove[ i ];\n\n this._deactivateAction( action );\n\n var cacheIndex = action._cacheIndex,\n lastInactiveAction = actions[ actions.length - 1 ];\n\n action._cacheIndex = null;\n action._byClipCacheIndex = null;\n\n lastInactiveAction._cacheIndex = cacheIndex;\n actions[ cacheIndex ] = lastInactiveAction;\n actions.pop();\n\n this._removeInactiveBindingsForAction( action );\n\n }\n\n delete actionsByClip[ clipUuid ];\n\n }\n\n },\n\n // free all resources specific to a particular root target object\n uncacheRoot: function ( root ) {\n\n var rootUuid = root.uuid,\n actionsByClip = this._actionsByClip;\n\n for ( var clipUuid in actionsByClip ) {\n\n var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,\n action = actionByRoot[ rootUuid ];\n\n if ( action !== undefined ) {\n\n this._deactivateAction( action );\n this._removeInactiveAction( action );\n\n }\n\n }\n\n var bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ];\n\n if ( bindingByName !== undefined ) {\n\n for ( var trackName in bindingByName ) {\n\n var binding = bindingByName[ trackName ];\n binding.restoreOriginalState();\n this._removeInactiveBinding( binding );\n\n }\n\n }\n\n },\n\n // remove a targeted clip from the cache\n uncacheAction: function ( clip, optionalRoot ) {\n\n var action = this.existingAction( clip, optionalRoot );\n\n if ( action !== null ) {\n\n this._deactivateAction( action );\n this._removeInactiveAction( action );\n\n }\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Uniform( value ) {\n\n if ( typeof value === 'string' ) {\n\n console.warn( 'THREE.Uniform: Type parameter is no longer needed.' );\n value = arguments[ 1 ];\n\n }\n\n this.value = value;\n\n }\n\n Uniform.prototype.clone = function () {\n\n return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );\n\n };\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedBufferGeometry() {\n\n BufferGeometry.call( this );\n\n this.type = 'InstancedBufferGeometry';\n this.maxInstancedCount = undefined;\n\n }\n\n InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), {\n\n constructor: InstancedBufferGeometry,\n\n isInstancedBufferGeometry: true,\n\n copy: function ( source ) {\n\n BufferGeometry.prototype.copy.call( this, source );\n\n this.maxInstancedCount = source.maxInstancedCount;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {\n\n this.data = interleavedBuffer;\n this.itemSize = itemSize;\n this.offset = offset;\n\n this.normalized = normalized === true;\n\n }\n\n Object.defineProperties( InterleavedBufferAttribute.prototype, {\n\n count: {\n\n get: function () {\n\n return this.data.count;\n\n }\n\n },\n\n array: {\n\n get: function () {\n\n return this.data.array;\n\n }\n\n }\n\n } );\n\n Object.assign( InterleavedBufferAttribute.prototype, {\n\n isInterleavedBufferAttribute: true,\n\n setX: function ( index, x ) {\n\n this.data.array[ index * this.data.stride + this.offset ] = x;\n\n return this;\n\n },\n\n setY: function ( index, y ) {\n\n this.data.array[ index * this.data.stride + this.offset + 1 ] = y;\n\n return this;\n\n },\n\n setZ: function ( index, z ) {\n\n this.data.array[ index * this.data.stride + this.offset + 2 ] = z;\n\n return this;\n\n },\n\n setW: function ( index, w ) {\n\n this.data.array[ index * this.data.stride + this.offset + 3 ] = w;\n\n return this;\n\n },\n\n getX: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset ];\n\n },\n\n getY: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 1 ];\n\n },\n\n getZ: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 2 ];\n\n },\n\n getW: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 3 ];\n\n },\n\n setXY: function ( index, x, y ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n\n return this;\n\n },\n\n setXYZ: function ( index, x, y, z ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n this.data.array[ index + 2 ] = z;\n\n return this;\n\n },\n\n setXYZW: function ( index, x, y, z, w ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n this.data.array[ index + 2 ] = z;\n this.data.array[ index + 3 ] = w;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InterleavedBuffer( array, stride ) {\n\n this.array = array;\n this.stride = stride;\n this.count = array !== undefined ? array.length / stride : 0;\n\n this.dynamic = false;\n this.updateRange = { offset: 0, count: - 1 };\n\n this.version = 0;\n\n }\n\n Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n Object.assign( InterleavedBuffer.prototype, {\n\n isInterleavedBuffer: true,\n\n onUploadCallback: function () {},\n\n setArray: function ( array ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.count = array !== undefined ? array.length / this.stride : 0;\n this.array = array;\n\n },\n\n setDynamic: function ( value ) {\n\n this.dynamic = value;\n\n return this;\n\n },\n\n copy: function ( source ) {\n\n this.array = new source.array.constructor( source.array );\n this.count = source.count;\n this.stride = source.stride;\n this.dynamic = source.dynamic;\n\n return this;\n\n },\n\n copyAt: function ( index1, attribute, index2 ) {\n\n index1 *= this.stride;\n index2 *= attribute.stride;\n\n for ( var i = 0, l = this.stride; i < l; i ++ ) {\n\n this.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n }\n\n return this;\n\n },\n\n set: function ( value, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.array.set( value, offset );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n onUpload: function ( callback ) {\n\n this.onUploadCallback = callback;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) {\n\n InterleavedBuffer.call( this, array, stride );\n\n this.meshPerAttribute = meshPerAttribute || 1;\n\n }\n\n InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), {\n\n constructor: InstancedInterleavedBuffer,\n\n isInstancedInterleavedBuffer: true,\n\n copy: function ( source ) {\n\n InterleavedBuffer.prototype.copy.call( this, source );\n\n this.meshPerAttribute = source.meshPerAttribute;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) {\n\n BufferAttribute.call( this, array, itemSize );\n\n this.meshPerAttribute = meshPerAttribute || 1;\n\n }\n\n InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), {\n\n constructor: InstancedBufferAttribute,\n\n isInstancedBufferAttribute: true,\n\n copy: function ( source ) {\n\n BufferAttribute.prototype.copy.call( this, source );\n\n this.meshPerAttribute = source.meshPerAttribute;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author bhouston / http://clara.io/\n * @author stephomi / http://stephaneginier.com/\n */\n\n function Raycaster( origin, direction, near, far ) {\n\n this.ray = new Ray( origin, direction );\n // direction is assumed to be normalized (for accurate distance calculations)\n\n this.near = near || 0;\n this.far = far || Infinity;\n\n this.params = {\n Mesh: {},\n Line: {},\n LOD: {},\n Points: { threshold: 1 },\n Sprite: {}\n };\n\n Object.defineProperties( this.params, {\n PointCloud: {\n get: function () {\n\n console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );\n return this.Points;\n\n }\n }\n } );\n\n }\n\n function ascSort( a, b ) {\n\n return a.distance - b.distance;\n\n }\n\n function intersectObject( object, raycaster, intersects, recursive ) {\n\n if ( object.visible === false ) return;\n\n object.raycast( raycaster, intersects );\n\n if ( recursive === true ) {\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n intersectObject( children[ i ], raycaster, intersects, true );\n\n }\n\n }\n\n }\n\n Object.assign( Raycaster.prototype, {\n\n linePrecision: 1,\n\n set: function ( origin, direction ) {\n\n // direction is assumed to be normalized (for accurate distance calculations)\n\n this.ray.set( origin, direction );\n\n },\n\n setFromCamera: function ( coords, camera ) {\n\n if ( ( camera && camera.isPerspectiveCamera ) ) {\n\n this.ray.origin.setFromMatrixPosition( camera.matrixWorld );\n this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();\n\n } else if ( ( camera && camera.isOrthographicCamera ) ) {\n\n this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera\n this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );\n\n } else {\n\n console.error( 'THREE.Raycaster: Unsupported camera type.' );\n\n }\n\n },\n\n intersectObject: function ( object, recursive, optionalTarget ) {\n\n var intersects = optionalTarget || [];\n\n intersectObject( object, this, intersects, recursive );\n\n intersects.sort( ascSort );\n\n return intersects;\n\n },\n\n intersectObjects: function ( objects, recursive, optionalTarget ) {\n\n var intersects = optionalTarget || [];\n\n if ( Array.isArray( objects ) === false ) {\n\n console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );\n return intersects;\n\n }\n\n for ( var i = 0, l = objects.length; i < l; i ++ ) {\n\n intersectObject( objects[ i ], this, intersects, recursive );\n\n }\n\n intersects.sort( ascSort );\n\n return intersects;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Clock( autoStart ) {\n\n this.autoStart = ( autoStart !== undefined ) ? autoStart : true;\n\n this.startTime = 0;\n this.oldTime = 0;\n this.elapsedTime = 0;\n\n this.running = false;\n\n }\n\n Object.assign( Clock.prototype, {\n\n start: function () {\n\n this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732\n\n this.oldTime = this.startTime;\n this.elapsedTime = 0;\n this.running = true;\n\n },\n\n stop: function () {\n\n this.getElapsedTime();\n this.running = false;\n this.autoStart = false;\n\n },\n\n getElapsedTime: function () {\n\n this.getDelta();\n return this.elapsedTime;\n\n },\n\n getDelta: function () {\n\n var diff = 0;\n\n if ( this.autoStart && ! this.running ) {\n\n this.start();\n return 0;\n\n }\n\n if ( this.running ) {\n\n var newTime = ( typeof performance === 'undefined' ? Date : performance ).now();\n\n diff = ( newTime - this.oldTime ) / 1000;\n this.oldTime = newTime;\n\n this.elapsedTime += diff;\n\n }\n\n return diff;\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n *\n * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system\n *\n * The poles (phi) are at the positive and negative y axis.\n * The equator starts at positive z.\n */\n\n function Spherical( radius, phi, theta ) {\n\n this.radius = ( radius !== undefined ) ? radius : 1.0;\n this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole\n this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere\n\n return this;\n\n }\n\n Object.assign( Spherical.prototype, {\n\n set: function ( radius, phi, theta ) {\n\n this.radius = radius;\n this.phi = phi;\n this.theta = theta;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( other ) {\n\n this.radius = other.radius;\n this.phi = other.phi;\n this.theta = other.theta;\n\n return this;\n\n },\n\n // restrict phi to be betwee EPS and PI-EPS\n makeSafe: function () {\n\n var EPS = 0.000001;\n this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) );\n\n return this;\n\n },\n\n setFromVector3: function ( vec3 ) {\n\n this.radius = vec3.length();\n\n if ( this.radius === 0 ) {\n\n this.theta = 0;\n this.phi = 0;\n\n } else {\n\n this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis\n this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n *\n * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system\n *\n */\n\n function Cylindrical( radius, theta, y ) {\n\n this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane\n this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis\n this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane\n\n return this;\n\n }\n\n Object.assign( Cylindrical.prototype, {\n\n set: function ( radius, theta, y ) {\n\n this.radius = radius;\n this.theta = theta;\n this.y = y;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( other ) {\n\n this.radius = other.radius;\n this.theta = other.theta;\n this.y = other.y;\n\n return this;\n\n },\n\n setFromVector3: function ( vec3 ) {\n\n this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z );\n this.theta = Math.atan2( vec3.x, vec3.z );\n this.y = vec3.y;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Box2( min, max ) {\n\n this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity );\n this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity );\n\n }\n\n Object.assign( Box2.prototype, {\n\n set: function ( min, max ) {\n\n this.min.copy( min );\n this.max.copy( max );\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.makeEmpty();\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n this.expandByPoint( points[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromCenterAndSize: function () {\n\n var v1 = new Vector2();\n\n return function setFromCenterAndSize( center, size ) {\n\n var halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n this.min.copy( center ).sub( halfSize );\n this.max.copy( center ).add( halfSize );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( box ) {\n\n this.min.copy( box.min );\n this.max.copy( box.max );\n\n return this;\n\n },\n\n makeEmpty: function () {\n\n this.min.x = this.min.y = + Infinity;\n this.max.x = this.max.y = - Infinity;\n\n return this;\n\n },\n\n isEmpty: function () {\n\n // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getCenter() target is now required' );\n target = new Vector2();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n },\n\n getSize: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getSize() target is now required' );\n target = new Vector2();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min );\n\n },\n\n expandByPoint: function ( point ) {\n\n this.min.min( point );\n this.max.max( point );\n\n return this;\n\n },\n\n expandByVector: function ( vector ) {\n\n this.min.sub( vector );\n this.max.add( vector );\n\n return this;\n\n },\n\n expandByScalar: function ( scalar ) {\n\n this.min.addScalar( - scalar );\n this.max.addScalar( scalar );\n\n return this;\n\n },\n\n containsPoint: function ( point ) {\n\n return point.x < this.min.x || point.x > this.max.x ||\n point.y < this.min.y || point.y > this.max.y ? false : true;\n\n },\n\n containsBox: function ( box ) {\n\n return this.min.x <= box.min.x && box.max.x <= this.max.x &&\n this.min.y <= box.min.y && box.max.y <= this.max.y;\n\n },\n\n getParameter: function ( point, target ) {\n\n // This can potentially have a divide by zero if the box\n // has a size dimension of 0.\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getParameter() target is now required' );\n target = new Vector2();\n\n }\n\n return target.set(\n ( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n ( point.y - this.min.y ) / ( this.max.y - this.min.y )\n );\n\n },\n\n intersectsBox: function ( box ) {\n\n // using 4 splitting planes to rule out intersections\n\n return box.max.x < this.min.x || box.min.x > this.max.x ||\n box.max.y < this.min.y || box.min.y > this.max.y ? false : true;\n\n },\n\n clampPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .clampPoint() target is now required' );\n target = new Vector2();\n\n }\n\n return target.copy( point ).clamp( this.min, this.max );\n\n },\n\n distanceToPoint: function () {\n\n var v1 = new Vector2();\n\n return function distanceToPoint( point ) {\n\n var clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n return clampedPoint.sub( point ).length();\n\n };\n\n }(),\n\n intersect: function ( box ) {\n\n this.min.max( box.min );\n this.max.min( box.max );\n\n return this;\n\n },\n\n union: function ( box ) {\n\n this.min.min( box.min );\n this.max.max( box.max );\n\n return this;\n\n },\n\n translate: function ( offset ) {\n\n this.min.add( offset );\n this.max.add( offset );\n\n return this;\n\n },\n\n equals: function ( box ) {\n\n return box.min.equals( this.min ) && box.max.equals( this.max );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function ImmediateRenderObject( material ) {\n\n Object3D.call( this );\n\n this.material = material;\n this.render = function ( /* renderCallback */ ) {};\n\n }\n\n ImmediateRenderObject.prototype = Object.create( Object3D.prototype );\n ImmediateRenderObject.prototype.constructor = ImmediateRenderObject;\n\n ImmediateRenderObject.prototype.isImmediateRenderObject = true;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function VertexNormalsHelper( object, size, hex, linewidth ) {\n\n this.object = object;\n\n this.size = ( size !== undefined ) ? size : 1;\n\n var color = ( hex !== undefined ) ? hex : 0xff0000;\n\n var width = ( linewidth !== undefined ) ? linewidth : 1;\n\n //\n\n var nNormals = 0;\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n nNormals = objGeometry.faces.length * 3;\n\n } else if ( objGeometry && objGeometry.isBufferGeometry ) {\n\n nNormals = objGeometry.attributes.normal.count;\n\n }\n\n //\n\n var geometry = new BufferGeometry();\n\n var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );\n\n geometry.addAttribute( 'position', positions );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );\n\n //\n\n this.matrixAutoUpdate = false;\n\n this.update();\n\n }\n\n VertexNormalsHelper.prototype = Object.create( LineSegments.prototype );\n VertexNormalsHelper.prototype.constructor = VertexNormalsHelper;\n\n VertexNormalsHelper.prototype.update = ( function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var normalMatrix = new Matrix3();\n\n return function update() {\n\n var keys = [ 'a', 'b', 'c' ];\n\n this.object.updateMatrixWorld( true );\n\n normalMatrix.getNormalMatrix( this.object.matrixWorld );\n\n var matrixWorld = this.object.matrixWorld;\n\n var position = this.geometry.attributes.position;\n\n //\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n var vertices = objGeometry.vertices;\n\n var faces = objGeometry.faces;\n\n var idx = 0;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {\n\n var vertex = vertices[ face[ keys[ j ] ] ];\n\n var normal = face.vertexNormals[ j ];\n\n v1.copy( vertex ).applyMatrix4( matrixWorld );\n\n v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n }\n\n } else if ( objGeometry && objGeometry.isBufferGeometry ) {\n\n var objPos = objGeometry.attributes.position;\n\n var objNorm = objGeometry.attributes.normal;\n\n var idx = 0;\n\n // for simplicity, ignore index and drawcalls, and render every normal\n\n for ( var j = 0, jl = objPos.count; j < jl; j ++ ) {\n\n v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld );\n\n v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) );\n\n v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n }\n\n position.needsUpdate = true;\n\n };\n\n }() );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function SpotLightHelper( light, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var geometry = new BufferGeometry();\n\n var positions = [\n 0, 0, 0, 0, 0, 1,\n 0, 0, 0, 1, 0, 1,\n 0, 0, 0, - 1, 0, 1,\n 0, 0, 0, 0, 1, 1,\n 0, 0, 0, 0, - 1, 1\n ];\n\n for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {\n\n var p1 = ( i / l ) * Math.PI * 2;\n var p2 = ( j / l ) * Math.PI * 2;\n\n positions.push(\n Math.cos( p1 ), Math.sin( p1 ), 1,\n Math.cos( p2 ), Math.sin( p2 ), 1\n );\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n var material = new LineBasicMaterial( { fog: false } );\n\n this.cone = new LineSegments( geometry, material );\n this.add( this.cone );\n\n this.update();\n\n }\n\n SpotLightHelper.prototype = Object.create( Object3D.prototype );\n SpotLightHelper.prototype.constructor = SpotLightHelper;\n\n SpotLightHelper.prototype.dispose = function () {\n\n this.cone.geometry.dispose();\n this.cone.material.dispose();\n\n };\n\n SpotLightHelper.prototype.update = function () {\n\n var vector = new Vector3();\n var vector2 = new Vector3();\n\n return function update() {\n\n this.light.updateMatrixWorld();\n\n var coneLength = this.light.distance ? this.light.distance : 1000;\n var coneWidth = coneLength * Math.tan( this.light.angle );\n\n this.cone.scale.set( coneWidth, coneWidth, coneLength );\n\n vector.setFromMatrixPosition( this.light.matrixWorld );\n vector2.setFromMatrixPosition( this.light.target.matrixWorld );\n\n this.cone.lookAt( vector2.sub( vector ) );\n\n if ( this.color !== undefined ) {\n\n this.cone.material.color.set( this.color );\n\n } else {\n\n this.cone.material.color.copy( this.light.color );\n\n }\n\n };\n\n }();\n\n /**\n * @author Sean Griffin / http://twitter.com/sgrif\n * @author Michael Guerrero / http://realitymeltdown.com\n * @author mrdoob / http://mrdoob.com/\n * @author ikerr / http://verold.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function getBoneList( object ) {\n\n var boneList = [];\n\n if ( object && object.isBone ) {\n\n boneList.push( object );\n\n }\n\n for ( var i = 0; i < object.children.length; i ++ ) {\n\n boneList.push.apply( boneList, getBoneList( object.children[ i ] ) );\n\n }\n\n return boneList;\n\n }\n\n function SkeletonHelper( object ) {\n\n var bones = getBoneList( object );\n\n var geometry = new BufferGeometry();\n\n var vertices = [];\n var colors = [];\n\n var color1 = new Color( 0, 0, 1 );\n var color2 = new Color( 0, 1, 0 );\n\n for ( var i = 0; i < bones.length; i ++ ) {\n\n var bone = bones[ i ];\n\n if ( bone.parent && bone.parent.isBone ) {\n\n vertices.push( 0, 0, 0 );\n vertices.push( 0, 0, 0 );\n colors.push( color1.r, color1.g, color1.b );\n colors.push( color2.r, color2.g, color2.b );\n\n }\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } );\n\n LineSegments.call( this, geometry, material );\n\n this.root = object;\n this.bones = bones;\n\n this.matrix = object.matrixWorld;\n this.matrixAutoUpdate = false;\n\n }\n\n SkeletonHelper.prototype = Object.create( LineSegments.prototype );\n SkeletonHelper.prototype.constructor = SkeletonHelper;\n\n SkeletonHelper.prototype.updateMatrixWorld = function () {\n\n var vector = new Vector3();\n\n var boneMatrix = new Matrix4();\n var matrixWorldInv = new Matrix4();\n\n return function updateMatrixWorld( force ) {\n\n var bones = this.bones;\n\n var geometry = this.geometry;\n var position = geometry.getAttribute( 'position' );\n\n matrixWorldInv.getInverse( this.root.matrixWorld );\n\n for ( var i = 0, j = 0; i < bones.length; i ++ ) {\n\n var bone = bones[ i ];\n\n if ( bone.parent && bone.parent.isBone ) {\n\n boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );\n vector.setFromMatrixPosition( boneMatrix );\n position.setXYZ( j, vector.x, vector.y, vector.z );\n\n boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );\n vector.setFromMatrixPosition( boneMatrix );\n position.setXYZ( j + 1, vector.x, vector.y, vector.z );\n\n j += 2;\n\n }\n\n }\n\n geometry.getAttribute( 'position' ).needsUpdate = true;\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n }();\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function PointLightHelper( light, sphereSize, color ) {\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.color = color;\n\n var geometry = new SphereBufferGeometry( sphereSize, 4, 2 );\n var material = new MeshBasicMaterial( { wireframe: true, fog: false } );\n\n Mesh.call( this, geometry, material );\n\n this.matrix = this.light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.update();\n\n\n /*\n var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );\n var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );\n\n this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );\n this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );\n\n var d = light.distance;\n\n if ( d === 0.0 ) {\n\n this.lightDistance.visible = false;\n\n } else {\n\n this.lightDistance.scale.set( d, d, d );\n\n }\n\n this.add( this.lightDistance );\n */\n\n }\n\n PointLightHelper.prototype = Object.create( Mesh.prototype );\n PointLightHelper.prototype.constructor = PointLightHelper;\n\n PointLightHelper.prototype.dispose = function () {\n\n this.geometry.dispose();\n this.material.dispose();\n\n };\n\n PointLightHelper.prototype.update = function () {\n\n if ( this.color !== undefined ) {\n\n this.material.color.set( this.color );\n\n } else {\n\n this.material.color.copy( this.light.color );\n\n }\n\n /*\n var d = this.light.distance;\n\n if ( d === 0.0 ) {\n\n this.lightDistance.visible = false;\n\n } else {\n\n this.lightDistance.visible = true;\n this.lightDistance.scale.set( d, d, d );\n\n }\n */\n\n };\n\n /**\n * @author abelnation / http://github.com/abelnation\n * @author Mugen87 / http://github.com/Mugen87\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function RectAreaLightHelper( light, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var material = new LineBasicMaterial( { fog: false } );\n\n var geometry = new BufferGeometry();\n\n geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) );\n\n this.line = new Line( geometry, material );\n this.add( this.line );\n\n\n this.update();\n\n }\n\n RectAreaLightHelper.prototype = Object.create( Object3D.prototype );\n RectAreaLightHelper.prototype.constructor = RectAreaLightHelper;\n\n RectAreaLightHelper.prototype.dispose = function () {\n\n this.children[ 0 ].geometry.dispose();\n this.children[ 0 ].material.dispose();\n\n };\n\n RectAreaLightHelper.prototype.update = function () {\n\n // calculate new dimensions of the helper\n\n var hx = this.light.width * 0.5;\n var hy = this.light.height * 0.5;\n\n var position = this.line.geometry.attributes.position;\n var array = position.array;\n\n // update vertices\n\n array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0;\n array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0;\n array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0;\n array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0;\n array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0;\n\n position.needsUpdate = true;\n\n if ( this.color !== undefined ) {\n\n this.line.material.color.set( this.color );\n\n } else {\n\n this.line.material.color.copy( this.light.color );\n\n }\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function HemisphereLightHelper( light, size, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var geometry = new OctahedronBufferGeometry( size );\n geometry.rotateY( Math.PI * 0.5 );\n\n this.material = new MeshBasicMaterial( { wireframe: true, fog: false } );\n if ( this.color === undefined ) this.material.vertexColors = VertexColors;\n\n var position = geometry.getAttribute( 'position' );\n var colors = new Float32Array( position.count * 3 );\n\n geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) );\n\n this.add( new Mesh( geometry, this.material ) );\n\n this.update();\n\n }\n\n HemisphereLightHelper.prototype = Object.create( Object3D.prototype );\n HemisphereLightHelper.prototype.constructor = HemisphereLightHelper;\n\n HemisphereLightHelper.prototype.dispose = function () {\n\n this.children[ 0 ].geometry.dispose();\n this.children[ 0 ].material.dispose();\n\n };\n\n HemisphereLightHelper.prototype.update = function () {\n\n var vector = new Vector3();\n\n var color1 = new Color();\n var color2 = new Color();\n\n return function update() {\n\n var mesh = this.children[ 0 ];\n\n if ( this.color !== undefined ) {\n\n this.material.color.set( this.color );\n\n } else {\n\n var colors = mesh.geometry.getAttribute( 'color' );\n\n color1.copy( this.light.color );\n color2.copy( this.light.groundColor );\n\n for ( var i = 0, l = colors.count; i < l; i ++ ) {\n\n var color = ( i < ( l / 2 ) ) ? color1 : color2;\n\n colors.setXYZ( i, color.r, color.g, color.b );\n\n }\n\n colors.needsUpdate = true;\n\n }\n\n mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );\n\n };\n\n }();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function GridHelper( size, divisions, color1, color2 ) {\n\n size = size || 10;\n divisions = divisions || 10;\n color1 = new Color( color1 !== undefined ? color1 : 0x444444 );\n color2 = new Color( color2 !== undefined ? color2 : 0x888888 );\n\n var center = divisions / 2;\n var step = size / divisions;\n var halfSize = size / 2;\n\n var vertices = [], colors = [];\n\n for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) {\n\n vertices.push( - halfSize, 0, k, halfSize, 0, k );\n vertices.push( k, 0, - halfSize, k, 0, halfSize );\n\n var color = i === center ? color1 : color2;\n\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n\n }\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n GridHelper.prototype = Object.create( LineSegments.prototype );\n GridHelper.prototype.constructor = GridHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / http://github.com/Mugen87\n * @author Hectate / http://www.github.com/Hectate\n */\n\n function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) {\n\n radius = radius || 10;\n radials = radials || 16;\n circles = circles || 8;\n divisions = divisions || 64;\n color1 = new Color( color1 !== undefined ? color1 : 0x444444 );\n color2 = new Color( color2 !== undefined ? color2 : 0x888888 );\n\n var vertices = [];\n var colors = [];\n\n var x, z;\n var v, i, j, r, color;\n\n // create the radials\n\n for ( i = 0; i <= radials; i ++ ) {\n\n v = ( i / radials ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * radius;\n z = Math.cos( v ) * radius;\n\n vertices.push( 0, 0, 0 );\n vertices.push( x, 0, z );\n\n color = ( i & 1 ) ? color1 : color2;\n\n colors.push( color.r, color.g, color.b );\n colors.push( color.r, color.g, color.b );\n\n }\n\n // create the circles\n\n for ( i = 0; i <= circles; i ++ ) {\n\n color = ( i & 1 ) ? color1 : color2;\n\n r = radius - ( radius / circles * i );\n\n for ( j = 0; j < divisions; j ++ ) {\n\n // first vertex\n\n v = ( j / divisions ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * r;\n z = Math.cos( v ) * r;\n\n vertices.push( x, 0, z );\n colors.push( color.r, color.g, color.b );\n\n // second vertex\n\n v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * r;\n z = Math.cos( v ) * r;\n\n vertices.push( x, 0, z );\n colors.push( color.r, color.g, color.b );\n\n }\n\n }\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n PolarGridHelper.prototype = Object.create( LineSegments.prototype );\n PolarGridHelper.prototype.constructor = PolarGridHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function FaceNormalsHelper( object, size, hex, linewidth ) {\n\n // FaceNormalsHelper only supports THREE.Geometry\n\n this.object = object;\n\n this.size = ( size !== undefined ) ? size : 1;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var width = ( linewidth !== undefined ) ? linewidth : 1;\n\n //\n\n var nNormals = 0;\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n nNormals = objGeometry.faces.length;\n\n } else {\n\n console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' );\n\n }\n\n //\n\n var geometry = new BufferGeometry();\n\n var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );\n\n geometry.addAttribute( 'position', positions );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );\n\n //\n\n this.matrixAutoUpdate = false;\n this.update();\n\n }\n\n FaceNormalsHelper.prototype = Object.create( LineSegments.prototype );\n FaceNormalsHelper.prototype.constructor = FaceNormalsHelper;\n\n FaceNormalsHelper.prototype.update = ( function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var normalMatrix = new Matrix3();\n\n return function update() {\n\n this.object.updateMatrixWorld( true );\n\n normalMatrix.getNormalMatrix( this.object.matrixWorld );\n\n var matrixWorld = this.object.matrixWorld;\n\n var position = this.geometry.attributes.position;\n\n //\n\n var objGeometry = this.object.geometry;\n\n var vertices = objGeometry.vertices;\n\n var faces = objGeometry.faces;\n\n var idx = 0;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n var normal = face.normal;\n\n v1.copy( vertices[ face.a ] )\n .add( vertices[ face.b ] )\n .add( vertices[ face.c ] )\n .divideScalar( 3 )\n .applyMatrix4( matrixWorld );\n\n v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n position.needsUpdate = true;\n\n };\n\n }() );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function DirectionalLightHelper( light, size, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n if ( size === undefined ) size = 1;\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( [\n - size, size, 0,\n size, size, 0,\n size, - size, 0,\n - size, - size, 0,\n - size, size, 0\n ], 3 ) );\n\n var material = new LineBasicMaterial( { fog: false } );\n\n this.lightPlane = new Line( geometry, material );\n this.add( this.lightPlane );\n\n geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );\n\n this.targetLine = new Line( geometry, material );\n this.add( this.targetLine );\n\n this.update();\n\n }\n\n DirectionalLightHelper.prototype = Object.create( Object3D.prototype );\n DirectionalLightHelper.prototype.constructor = DirectionalLightHelper;\n\n DirectionalLightHelper.prototype.dispose = function () {\n\n this.lightPlane.geometry.dispose();\n this.lightPlane.material.dispose();\n this.targetLine.geometry.dispose();\n this.targetLine.material.dispose();\n\n };\n\n DirectionalLightHelper.prototype.update = function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var v3 = new Vector3();\n\n return function update() {\n\n v1.setFromMatrixPosition( this.light.matrixWorld );\n v2.setFromMatrixPosition( this.light.target.matrixWorld );\n v3.subVectors( v2, v1 );\n\n this.lightPlane.lookAt( v3 );\n\n if ( this.color !== undefined ) {\n\n this.lightPlane.material.color.set( this.color );\n this.targetLine.material.color.set( this.color );\n\n } else {\n\n this.lightPlane.material.color.copy( this.light.color );\n this.targetLine.material.color.copy( this.light.color );\n\n }\n\n this.targetLine.lookAt( v3 );\n this.targetLine.scale.z = v3.length();\n\n };\n\n }();\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author Mugen87 / https://github.com/Mugen87\n *\n * - shows frustum, line of sight and up of the camera\n * - suitable for fast updates\n * - based on frustum visualization in lightgl.js shadowmap example\n * http://evanw.github.com/lightgl.js/tests/shadowmap.html\n */\n\n function CameraHelper( camera ) {\n\n var geometry = new BufferGeometry();\n var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } );\n\n var vertices = [];\n var colors = [];\n\n var pointMap = {};\n\n // colors\n\n var colorFrustum = new Color( 0xffaa00 );\n var colorCone = new Color( 0xff0000 );\n var colorUp = new Color( 0x00aaff );\n var colorTarget = new Color( 0xffffff );\n var colorCross = new Color( 0x333333 );\n\n // near\n\n addLine( 'n1', 'n2', colorFrustum );\n addLine( 'n2', 'n4', colorFrustum );\n addLine( 'n4', 'n3', colorFrustum );\n addLine( 'n3', 'n1', colorFrustum );\n\n // far\n\n addLine( 'f1', 'f2', colorFrustum );\n addLine( 'f2', 'f4', colorFrustum );\n addLine( 'f4', 'f3', colorFrustum );\n addLine( 'f3', 'f1', colorFrustum );\n\n // sides\n\n addLine( 'n1', 'f1', colorFrustum );\n addLine( 'n2', 'f2', colorFrustum );\n addLine( 'n3', 'f3', colorFrustum );\n addLine( 'n4', 'f4', colorFrustum );\n\n // cone\n\n addLine( 'p', 'n1', colorCone );\n addLine( 'p', 'n2', colorCone );\n addLine( 'p', 'n3', colorCone );\n addLine( 'p', 'n4', colorCone );\n\n // up\n\n addLine( 'u1', 'u2', colorUp );\n addLine( 'u2', 'u3', colorUp );\n addLine( 'u3', 'u1', colorUp );\n\n // target\n\n addLine( 'c', 't', colorTarget );\n addLine( 'p', 'c', colorCross );\n\n // cross\n\n addLine( 'cn1', 'cn2', colorCross );\n addLine( 'cn3', 'cn4', colorCross );\n\n addLine( 'cf1', 'cf2', colorCross );\n addLine( 'cf3', 'cf4', colorCross );\n\n function addLine( a, b, color ) {\n\n addPoint( a, color );\n addPoint( b, color );\n\n }\n\n function addPoint( id, color ) {\n\n vertices.push( 0, 0, 0 );\n colors.push( color.r, color.g, color.b );\n\n if ( pointMap[ id ] === undefined ) {\n\n pointMap[ id ] = [];\n\n }\n\n pointMap[ id ].push( ( vertices.length / 3 ) - 1 );\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n LineSegments.call( this, geometry, material );\n\n this.camera = camera;\n if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix();\n\n this.matrix = camera.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.pointMap = pointMap;\n\n this.update();\n\n }\n\n CameraHelper.prototype = Object.create( LineSegments.prototype );\n CameraHelper.prototype.constructor = CameraHelper;\n\n CameraHelper.prototype.update = function () {\n\n var geometry, pointMap;\n\n var vector = new Vector3();\n var camera = new Camera();\n\n function setPoint( point, x, y, z ) {\n\n vector.set( x, y, z ).unproject( camera );\n\n var points = pointMap[ point ];\n\n if ( points !== undefined ) {\n\n var position = geometry.getAttribute( 'position' );\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n position.setXYZ( points[ i ], vector.x, vector.y, vector.z );\n\n }\n\n }\n\n }\n\n return function update() {\n\n geometry = this.geometry;\n pointMap = this.pointMap;\n\n var w = 1, h = 1;\n\n // we need just camera projection matrix\n // world matrix must be identity\n\n camera.projectionMatrix.copy( this.camera.projectionMatrix );\n\n // center / target\n\n setPoint( 'c', 0, 0, - 1 );\n setPoint( 't', 0, 0, 1 );\n\n // near\n\n setPoint( 'n1', - w, - h, - 1 );\n setPoint( 'n2', w, - h, - 1 );\n setPoint( 'n3', - w, h, - 1 );\n setPoint( 'n4', w, h, - 1 );\n\n // far\n\n setPoint( 'f1', - w, - h, 1 );\n setPoint( 'f2', w, - h, 1 );\n setPoint( 'f3', - w, h, 1 );\n setPoint( 'f4', w, h, 1 );\n\n // up\n\n setPoint( 'u1', w * 0.7, h * 1.1, - 1 );\n setPoint( 'u2', - w * 0.7, h * 1.1, - 1 );\n setPoint( 'u3', 0, h * 2, - 1 );\n\n // cross\n\n setPoint( 'cf1', - w, 0, 1 );\n setPoint( 'cf2', w, 0, 1 );\n setPoint( 'cf3', 0, - h, 1 );\n setPoint( 'cf4', 0, h, 1 );\n\n setPoint( 'cn1', - w, 0, - 1 );\n setPoint( 'cn2', w, 0, - 1 );\n setPoint( 'cn3', 0, - h, - 1 );\n setPoint( 'cn4', 0, h, - 1 );\n\n geometry.getAttribute( 'position' ).needsUpdate = true;\n\n };\n\n }();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / http://github.com/Mugen87\n */\n\n function BoxHelper( object, color ) {\n\n this.object = object;\n\n if ( color === undefined ) color = 0xffff00;\n\n var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n var positions = new Float32Array( 8 * 3 );\n\n var geometry = new BufferGeometry();\n geometry.setIndex( new BufferAttribute( indices, 1 ) );\n geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n this.matrixAutoUpdate = false;\n\n this.update();\n\n }\n\n BoxHelper.prototype = Object.create( LineSegments.prototype );\n BoxHelper.prototype.constructor = BoxHelper;\n\n BoxHelper.prototype.update = ( function () {\n\n var box = new Box3();\n\n return function update( object ) {\n\n if ( object !== undefined ) {\n\n console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' );\n\n }\n\n if ( this.object !== undefined ) {\n\n box.setFromObject( this.object );\n\n }\n\n if ( box.isEmpty() ) return;\n\n var min = box.min;\n var max = box.max;\n\n /*\n 5____4\n 1/___0/|\n | 6__|_7\n 2/___3/\n\n 0: max.x, max.y, max.z\n 1: min.x, max.y, max.z\n 2: min.x, min.y, max.z\n 3: max.x, min.y, max.z\n 4: max.x, max.y, min.z\n 5: min.x, max.y, min.z\n 6: min.x, min.y, min.z\n 7: max.x, min.y, min.z\n */\n\n var position = this.geometry.attributes.position;\n var array = position.array;\n\n array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;\n array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;\n array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;\n array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;\n array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;\n array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;\n array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;\n array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;\n\n position.needsUpdate = true;\n\n this.geometry.computeBoundingSphere();\n\n };\n\n } )();\n\n BoxHelper.prototype.setFromObject = function ( object ) {\n\n this.object = object;\n this.update();\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Box3Helper( box, hex ) {\n\n this.type = 'Box3Helper';\n\n this.box = box;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n\n var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];\n\n var geometry = new BufferGeometry();\n\n geometry.setIndex( new BufferAttribute( indices, 1 ) );\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n this.geometry.computeBoundingSphere();\n\n }\n\n Box3Helper.prototype = Object.create( LineSegments.prototype );\n Box3Helper.prototype.constructor = Box3Helper;\n\n Box3Helper.prototype.updateMatrixWorld = function ( force ) {\n\n var box = this.box;\n\n if ( box.isEmpty() ) return;\n\n box.getCenter( this.position );\n\n box.getSize( this.scale );\n\n this.scale.multiplyScalar( 0.5 );\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function PlaneHelper( plane, size, hex ) {\n\n this.type = 'PlaneHelper';\n\n this.plane = plane;\n\n this.size = ( size === undefined ) ? 1 : size;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ];\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n geometry.computeBoundingSphere();\n\n Line.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n //\n\n var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ];\n\n var geometry2 = new BufferGeometry();\n geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );\n geometry2.computeBoundingSphere();\n\n this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) );\n\n }\n\n PlaneHelper.prototype = Object.create( Line.prototype );\n PlaneHelper.prototype.constructor = PlaneHelper;\n\n PlaneHelper.prototype.updateMatrixWorld = function ( force ) {\n\n var scale = - this.plane.constant;\n\n if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter\n\n this.scale.set( 0.5 * this.size, 0.5 * this.size, scale );\n\n this.lookAt( this.plane.normal );\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n * @author zz85 / http://github.com/zz85\n * @author bhouston / http://clara.io\n *\n * Creates an arrow for visualizing directions\n *\n * Parameters:\n * dir - Vector3\n * origin - Vector3\n * length - Number\n * color - color in hex value\n * headLength - Number\n * headWidth - Number\n */\n\n var lineGeometry;\n var coneGeometry;\n\n function ArrowHelper( dir, origin, length, color, headLength, headWidth ) {\n\n // dir is assumed to be normalized\n\n Object3D.call( this );\n\n if ( color === undefined ) color = 0xffff00;\n if ( length === undefined ) length = 1;\n if ( headLength === undefined ) headLength = 0.2 * length;\n if ( headWidth === undefined ) headWidth = 0.2 * headLength;\n\n if ( lineGeometry === undefined ) {\n\n lineGeometry = new BufferGeometry();\n lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );\n\n coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 );\n coneGeometry.translate( 0, - 0.5, 0 );\n\n }\n\n this.position.copy( origin );\n\n this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) );\n this.line.matrixAutoUpdate = false;\n this.add( this.line );\n\n this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) );\n this.cone.matrixAutoUpdate = false;\n this.add( this.cone );\n\n this.setDirection( dir );\n this.setLength( length, headLength, headWidth );\n\n }\n\n ArrowHelper.prototype = Object.create( Object3D.prototype );\n ArrowHelper.prototype.constructor = ArrowHelper;\n\n ArrowHelper.prototype.setDirection = ( function () {\n\n var axis = new Vector3();\n var radians;\n\n return function setDirection( dir ) {\n\n // dir is assumed to be normalized\n\n if ( dir.y > 0.99999 ) {\n\n this.quaternion.set( 0, 0, 0, 1 );\n\n } else if ( dir.y < - 0.99999 ) {\n\n this.quaternion.set( 1, 0, 0, 0 );\n\n } else {\n\n axis.set( dir.z, 0, - dir.x ).normalize();\n\n radians = Math.acos( dir.y );\n\n this.quaternion.setFromAxisAngle( axis, radians );\n\n }\n\n };\n\n }() );\n\n ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {\n\n if ( headLength === undefined ) headLength = 0.2 * length;\n if ( headWidth === undefined ) headWidth = 0.2 * headLength;\n\n this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 );\n this.line.updateMatrix();\n\n this.cone.scale.set( headWidth, headLength, headWidth );\n this.cone.position.y = length;\n this.cone.updateMatrix();\n\n };\n\n ArrowHelper.prototype.setColor = function ( color ) {\n\n this.line.material.color.copy( color );\n this.cone.material.color.copy( color );\n\n };\n\n /**\n * @author sroucheray / http://sroucheray.org/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AxesHelper( size ) {\n\n size = size || 1;\n\n var vertices = [\n 0, 0, 0, size, 0, 0,\n 0, 0, 0, 0, size, 0,\n 0, 0, 0, 0, 0, size\n ];\n\n var colors = [\n 1, 0, 0, 1, 0.6, 0,\n 0, 1, 0, 0.6, 1, 0,\n 0, 0, 1, 0, 0.6, 1\n ];\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n AxesHelper.prototype = Object.create( LineSegments.prototype );\n AxesHelper.prototype.constructor = AxesHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Face4( a, b, c, d, normal, color, materialIndex ) {\n\n console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );\n return new Face3( a, b, c, normal, color, materialIndex );\n\n }\n\n var LineStrip = 0;\n\n var LinePieces = 1;\n\n function MeshFaceMaterial( materials ) {\n\n console.warn( 'THREE.MeshFaceMaterial has been removed. Use an Array instead.' );\n return materials;\n\n }\n\n function MultiMaterial( materials ) {\n\n if ( materials === undefined ) materials = [];\n\n console.warn( 'THREE.MultiMaterial has been removed. Use an Array instead.' );\n materials.isMultiMaterial = true;\n materials.materials = materials;\n materials.clone = function () {\n\n return materials.slice();\n\n };\n return materials;\n\n }\n\n function PointCloud( geometry, material ) {\n\n console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );\n return new Points( geometry, material );\n\n }\n\n function Particle( material ) {\n\n console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' );\n return new Sprite( material );\n\n }\n\n function ParticleSystem( geometry, material ) {\n\n console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );\n return new Points( geometry, material );\n\n }\n\n function PointCloudMaterial( parameters ) {\n\n console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function ParticleBasicMaterial( parameters ) {\n\n console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function ParticleSystemMaterial( parameters ) {\n\n console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function Vertex( x, y, z ) {\n\n console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' );\n return new Vector3( x, y, z );\n\n }\n\n //\n\n function DynamicBufferAttribute( array, itemSize ) {\n\n console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );\n return new BufferAttribute( array, itemSize ).setDynamic( true );\n\n }\n\n function Int8Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' );\n return new Int8BufferAttribute( array, itemSize );\n\n }\n\n function Uint8Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' );\n return new Uint8BufferAttribute( array, itemSize );\n\n }\n\n function Uint8ClampedAttribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' );\n return new Uint8ClampedBufferAttribute( array, itemSize );\n\n }\n\n function Int16Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' );\n return new Int16BufferAttribute( array, itemSize );\n\n }\n\n function Uint16Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' );\n return new Uint16BufferAttribute( array, itemSize );\n\n }\n\n function Int32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' );\n return new Int32BufferAttribute( array, itemSize );\n\n }\n\n function Uint32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' );\n return new Uint32BufferAttribute( array, itemSize );\n\n }\n\n function Float32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' );\n return new Float32BufferAttribute( array, itemSize );\n\n }\n\n function Float64Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' );\n return new Float64BufferAttribute( array, itemSize );\n\n }\n\n //\n\n Curve.create = function ( construct, getPoint ) {\n\n console.log( 'THREE.Curve.create() has been deprecated' );\n\n construct.prototype = Object.create( Curve.prototype );\n construct.prototype.constructor = construct;\n construct.prototype.getPoint = getPoint;\n\n return construct;\n\n };\n\n //\n\n Object.assign( CurvePath.prototype, {\n\n createPointsGeometry: function ( divisions ) {\n\n console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n // generate geometry from path points (for Line or Points objects)\n\n var pts = this.getPoints( divisions );\n return this.createGeometry( pts );\n\n },\n\n createSpacedPointsGeometry: function ( divisions ) {\n\n console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n // generate geometry from equidistant sampling along the path\n\n var pts = this.getSpacedPoints( divisions );\n return this.createGeometry( pts );\n\n },\n\n createGeometry: function ( points ) {\n\n console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n var geometry = new Geometry();\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );\n\n }\n\n return geometry;\n\n }\n\n } );\n\n //\n\n Object.assign( Path.prototype, {\n\n fromPoints: function ( points ) {\n\n console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' );\n this.setFromPoints( points );\n\n }\n\n } );\n\n //\n\n function ClosedSplineCurve3( points ) {\n\n console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n this.closed = true;\n\n }\n\n ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );\n\n //\n\n function SplineCurve3( points ) {\n\n console.warn( 'THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n\n }\n\n SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );\n\n //\n\n function Spline( points ) {\n\n console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n\n }\n\n Spline.prototype = Object.create( CatmullRomCurve3.prototype );\n\n Object.assign( Spline.prototype, {\n\n initFromArray: function ( /* a */ ) {\n\n console.error( 'THREE.Spline: .initFromArray() has been removed.' );\n\n },\n getControlPointsArray: function ( /* optionalTarget */ ) {\n\n console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' );\n\n },\n reparametrizeByArcLength: function ( /* samplingCoef */ ) {\n\n console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' );\n\n }\n\n } );\n\n //\n\n function AxisHelper( size ) {\n\n console.warn( 'THREE.AxisHelper has been renamed to THREE.AxesHelper.' );\n return new AxesHelper( size );\n\n }\n\n function BoundingBoxHelper( object, color ) {\n\n console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' );\n return new BoxHelper( object, color );\n\n }\n\n function EdgesHelper( object, hex ) {\n\n console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' );\n return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );\n\n }\n\n GridHelper.prototype.setColors = function () {\n\n console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' );\n\n };\n\n SkeletonHelper.prototype.update = function () {\n\n console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' );\n\n };\n\n function WireframeHelper( object, hex ) {\n\n console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' );\n return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );\n\n }\n\n //\n\n Object.assign( Loader.prototype, {\n\n extractUrlBase: function ( url ) {\n\n console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' );\n return LoaderUtils.extractUrlBase( url );\n\n }\n\n } );\n\n function XHRLoader( manager ) {\n\n console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' );\n return new FileLoader( manager );\n\n }\n\n function BinaryTextureLoader( manager ) {\n\n console.warn( 'THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.' );\n return new DataTextureLoader( manager );\n\n }\n\n //\n\n Object.assign( Box2.prototype, {\n\n center: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n },\n empty: function () {\n\n console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' );\n return this.isEmpty();\n\n },\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n size: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' );\n return this.getSize( optionalTarget );\n\n }\n } );\n\n Object.assign( Box3.prototype, {\n\n center: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n },\n empty: function () {\n\n console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' );\n return this.isEmpty();\n\n },\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n isIntersectionSphere: function ( sphere ) {\n\n console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' );\n return this.intersectsSphere( sphere );\n\n },\n size: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' );\n return this.getSize( optionalTarget );\n\n }\n } );\n\n Line3.prototype.center = function ( optionalTarget ) {\n\n console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n };\n\n Object.assign( _Math, {\n\n random16: function () {\n\n console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' );\n return Math.random();\n\n },\n\n nearestPowerOfTwo: function ( value ) {\n\n console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' );\n return _Math.floorPowerOfTwo( value );\n\n },\n\n nextPowerOfTwo: function ( value ) {\n\n console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' );\n return _Math.ceilPowerOfTwo( value );\n\n }\n\n } );\n\n Object.assign( Matrix3.prototype, {\n\n flattenToArrayOffset: function ( array, offset ) {\n\n console.warn( \"THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.\" );\n return this.toArray( array, offset );\n\n },\n multiplyVector3: function ( vector ) {\n\n console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );\n return vector.applyMatrix3( this );\n\n },\n multiplyVector3Array: function ( /* a */ ) {\n\n console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' );\n\n },\n applyToBuffer: function ( buffer /*, offset, length */ ) {\n\n console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );\n return this.applyToBufferAttribute( buffer );\n\n },\n applyToVector3Array: function ( /* array, offset, length */ ) {\n\n console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' );\n\n }\n\n } );\n\n Object.assign( Matrix4.prototype, {\n\n extractPosition: function ( m ) {\n\n console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );\n return this.copyPosition( m );\n\n },\n flattenToArrayOffset: function ( array, offset ) {\n\n console.warn( \"THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.\" );\n return this.toArray( array, offset );\n\n },\n getPosition: function () {\n\n var v1;\n\n return function getPosition() {\n\n if ( v1 === undefined ) v1 = new Vector3();\n console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );\n return v1.setFromMatrixColumn( this, 3 );\n\n };\n\n }(),\n setRotationFromQuaternion: function ( q ) {\n\n console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );\n return this.makeRotationFromQuaternion( q );\n\n },\n multiplyToArray: function () {\n\n console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' );\n\n },\n multiplyVector3: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n multiplyVector4: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n multiplyVector3Array: function ( /* a */ ) {\n\n console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' );\n\n },\n rotateAxis: function ( v ) {\n\n console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );\n v.transformDirection( this );\n\n },\n crossVector: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n translate: function () {\n\n console.error( 'THREE.Matrix4: .translate() has been removed.' );\n\n },\n rotateX: function () {\n\n console.error( 'THREE.Matrix4: .rotateX() has been removed.' );\n\n },\n rotateY: function () {\n\n console.error( 'THREE.Matrix4: .rotateY() has been removed.' );\n\n },\n rotateZ: function () {\n\n console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );\n\n },\n rotateByAxis: function () {\n\n console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );\n\n },\n applyToBuffer: function ( buffer /*, offset, length */ ) {\n\n console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );\n return this.applyToBufferAttribute( buffer );\n\n },\n applyToVector3Array: function ( /* array, offset, length */ ) {\n\n console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' );\n\n },\n makeFrustum: function ( left, right, bottom, top, near, far ) {\n\n console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' );\n return this.makePerspective( left, right, top, bottom, near, far );\n\n }\n\n } );\n\n Plane.prototype.isIntersectionLine = function ( line ) {\n\n console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' );\n return this.intersectsLine( line );\n\n };\n\n Quaternion.prototype.multiplyVector3 = function ( vector ) {\n\n console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );\n return vector.applyQuaternion( this );\n\n };\n\n Object.assign( Ray.prototype, {\n\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n isIntersectionPlane: function ( plane ) {\n\n console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );\n return this.intersectsPlane( plane );\n\n },\n isIntersectionSphere: function ( sphere ) {\n\n console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );\n return this.intersectsSphere( sphere );\n\n }\n\n } );\n\n Object.assign( Triangle.prototype, {\n\n area: function () {\n\n console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' );\n return this.getArea();\n\n },\n barycoordFromPoint: function ( point, target ) {\n\n console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );\n return this.getBarycoord( point, target );\n\n },\n midpoint: function ( target ) {\n\n console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' );\n return this.getMidpoint( target );\n\n },\n normal: function ( target ) {\n\n console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );\n return this.getNormal( target );\n\n },\n plane: function ( target ) {\n\n console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' );\n return this.getPlane( target );\n\n }\n\n } );\n\n Object.assign( Triangle, {\n\n barycoordFromPoint: function ( point, a, b, c, target ) {\n\n console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );\n return Triangle.getBarycoord( point, a, b, c, target );\n\n },\n normal: function ( a, b, c, target ) {\n\n console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );\n return Triangle.getNormal( a, b, c, target );\n\n }\n\n } );\n\n Object.assign( Shape.prototype, {\n\n extractAllPoints: function ( divisions ) {\n\n console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' );\n return this.extractPoints( divisions );\n\n },\n extrude: function ( options ) {\n\n console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' );\n return new ExtrudeGeometry( this, options );\n\n },\n makeGeometry: function ( options ) {\n\n console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' );\n return new ShapeGeometry( this, options );\n\n }\n\n } );\n\n Object.assign( Vector2.prototype, {\n\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n distanceToManhattan: function ( v ) {\n\n console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );\n return this.manhattanDistanceTo( v );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n Object.assign( Vector3.prototype, {\n\n setEulerFromRotationMatrix: function () {\n\n console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );\n\n },\n setEulerFromQuaternion: function () {\n\n console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );\n\n },\n getPositionFromMatrix: function ( m ) {\n\n console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );\n return this.setFromMatrixPosition( m );\n\n },\n getScaleFromMatrix: function ( m ) {\n\n console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );\n return this.setFromMatrixScale( m );\n\n },\n getColumnFromMatrix: function ( index, matrix ) {\n\n console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );\n return this.setFromMatrixColumn( matrix, index );\n\n },\n applyProjection: function ( m ) {\n\n console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' );\n return this.applyMatrix4( m );\n\n },\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n distanceToManhattan: function ( v ) {\n\n console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );\n return this.manhattanDistanceTo( v );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n Object.assign( Vector4.prototype, {\n\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n //\n\n Object.assign( Geometry.prototype, {\n\n computeTangents: function () {\n\n console.error( 'THREE.Geometry: .computeTangents() has been removed.' );\n\n },\n computeLineDistances: function () {\n\n console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' );\n\n }\n\n } );\n\n Object.assign( Object3D.prototype, {\n\n getChildByName: function ( name ) {\n\n console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );\n return this.getObjectByName( name );\n\n },\n renderDepth: function () {\n\n console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );\n\n },\n translate: function ( distance, axis ) {\n\n console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );\n return this.translateOnAxis( axis, distance );\n\n },\n getWorldRotation: function () {\n\n console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' );\n\n }\n\n } );\n\n Object.defineProperties( Object3D.prototype, {\n\n eulerOrder: {\n get: function () {\n\n console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );\n return this.rotation.order;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );\n this.rotation.order = value;\n\n }\n },\n useQuaternion: {\n get: function () {\n\n console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );\n\n }\n }\n\n } );\n\n Object.defineProperties( LOD.prototype, {\n\n objects: {\n get: function () {\n\n console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );\n return this.levels;\n\n }\n }\n\n } );\n\n Object.defineProperty( Skeleton.prototype, 'useVertexTexture', {\n\n get: function () {\n\n console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );\n\n }\n\n } );\n\n Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {\n\n get: function () {\n\n console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );\n return this.arcLengthDivisions;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );\n this.arcLengthDivisions = value;\n\n }\n\n } );\n\n //\n\n PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {\n\n console.warn( \"THREE.PerspectiveCamera.setLens is deprecated. \" +\n \"Use .setFocalLength and .filmGauge for a photographic setup.\" );\n\n if ( filmGauge !== undefined ) this.filmGauge = filmGauge;\n this.setFocalLength( focalLength );\n\n };\n\n //\n\n Object.defineProperties( Light.prototype, {\n onlyShadow: {\n set: function () {\n\n console.warn( 'THREE.Light: .onlyShadow has been removed.' );\n\n }\n },\n shadowCameraFov: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' );\n this.shadow.camera.fov = value;\n\n }\n },\n shadowCameraLeft: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' );\n this.shadow.camera.left = value;\n\n }\n },\n shadowCameraRight: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' );\n this.shadow.camera.right = value;\n\n }\n },\n shadowCameraTop: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' );\n this.shadow.camera.top = value;\n\n }\n },\n shadowCameraBottom: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' );\n this.shadow.camera.bottom = value;\n\n }\n },\n shadowCameraNear: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' );\n this.shadow.camera.near = value;\n\n }\n },\n shadowCameraFar: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' );\n this.shadow.camera.far = value;\n\n }\n },\n shadowCameraVisible: {\n set: function () {\n\n console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' );\n\n }\n },\n shadowBias: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' );\n this.shadow.bias = value;\n\n }\n },\n shadowDarkness: {\n set: function () {\n\n console.warn( 'THREE.Light: .shadowDarkness has been removed.' );\n\n }\n },\n shadowMapWidth: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' );\n this.shadow.mapSize.width = value;\n\n }\n },\n shadowMapHeight: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' );\n this.shadow.mapSize.height = value;\n\n }\n }\n } );\n\n //\n\n Object.defineProperties( BufferAttribute.prototype, {\n\n length: {\n get: function () {\n\n console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' );\n return this.array.length;\n\n }\n },\n copyIndicesArray: function ( /* indices */ ) {\n\n console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' );\n\n }\n\n } );\n\n Object.assign( BufferGeometry.prototype, {\n\n addIndex: function ( index ) {\n\n console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );\n this.setIndex( index );\n\n },\n addDrawCall: function ( start, count, indexOffset ) {\n\n if ( indexOffset !== undefined ) {\n\n console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );\n\n }\n console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );\n this.addGroup( start, count );\n\n },\n clearDrawCalls: function () {\n\n console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );\n this.clearGroups();\n\n },\n computeTangents: function () {\n\n console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );\n\n },\n computeOffsets: function () {\n\n console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' );\n\n }\n\n } );\n\n Object.defineProperties( BufferGeometry.prototype, {\n\n drawcalls: {\n get: function () {\n\n console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );\n return this.groups;\n\n }\n },\n offsets: {\n get: function () {\n\n console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );\n return this.groups;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( Uniform.prototype, {\n\n dynamic: {\n set: function () {\n\n console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' );\n\n }\n },\n onUpdate: {\n value: function () {\n\n console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' );\n return this;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( Material.prototype, {\n\n wrapAround: {\n get: function () {\n\n console.warn( 'THREE.Material: .wrapAround has been removed.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Material: .wrapAround has been removed.' );\n\n }\n },\n wrapRGB: {\n get: function () {\n\n console.warn( 'THREE.Material: .wrapRGB has been removed.' );\n return new Color();\n\n }\n },\n\n shading: {\n get: function () {\n\n console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n this.flatShading = ( value === FlatShading );\n\n }\n }\n\n } );\n\n Object.defineProperties( MeshPhongMaterial.prototype, {\n\n metal: {\n get: function () {\n\n console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' );\n return false;\n\n },\n set: function () {\n\n console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' );\n\n }\n }\n\n } );\n\n Object.defineProperties( ShaderMaterial.prototype, {\n\n derivatives: {\n get: function () {\n\n console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );\n return this.extensions.derivatives;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );\n this.extensions.derivatives = value;\n\n }\n }\n\n } );\n\n //\n\n Object.assign( WebGLRenderer.prototype, {\n\n getCurrentRenderTarget: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' );\n return this.getRenderTarget();\n\n },\n\n getMaxAnisotropy: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' );\n return this.capabilities.getMaxAnisotropy();\n\n },\n\n getPrecision: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' );\n return this.capabilities.precision;\n\n },\n\n resetGLState: function () {\n\n console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' );\n return this.state.reset();\n\n },\n\n supportsFloatTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \\'OES_texture_float\\' ).' );\n return this.extensions.get( 'OES_texture_float' );\n\n },\n supportsHalfFloatTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \\'OES_texture_half_float\\' ).' );\n return this.extensions.get( 'OES_texture_half_float' );\n\n },\n supportsStandardDerivatives: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \\'OES_standard_derivatives\\' ).' );\n return this.extensions.get( 'OES_standard_derivatives' );\n\n },\n supportsCompressedTextureS3TC: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \\'WEBGL_compressed_texture_s3tc\\' ).' );\n return this.extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n },\n supportsCompressedTexturePVRTC: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \\'WEBGL_compressed_texture_pvrtc\\' ).' );\n return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n },\n supportsBlendMinMax: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \\'EXT_blend_minmax\\' ).' );\n return this.extensions.get( 'EXT_blend_minmax' );\n\n },\n supportsVertexTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' );\n return this.capabilities.vertexTextures;\n\n },\n supportsInstancedArrays: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \\'ANGLE_instanced_arrays\\' ).' );\n return this.extensions.get( 'ANGLE_instanced_arrays' );\n\n },\n enableScissorTest: function ( boolean ) {\n\n console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' );\n this.setScissorTest( boolean );\n\n },\n initMaterial: function () {\n\n console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );\n\n },\n addPrePlugin: function () {\n\n console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );\n\n },\n addPostPlugin: function () {\n\n console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );\n\n },\n updateShadowMap: function () {\n\n console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );\n\n },\n setFaceCulling: function () {\n\n console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' );\n\n }\n\n } );\n\n Object.defineProperties( WebGLRenderer.prototype, {\n\n shadowMapEnabled: {\n get: function () {\n\n return this.shadowMap.enabled;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );\n this.shadowMap.enabled = value;\n\n }\n },\n shadowMapType: {\n get: function () {\n\n return this.shadowMap.type;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );\n this.shadowMap.type = value;\n\n }\n },\n shadowMapCullFace: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function ( /* value */ ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );\n\n }\n }\n } );\n\n Object.defineProperties( WebGLShadowMap.prototype, {\n\n cullFace: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function ( /* cullFace */ ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );\n\n }\n },\n renderReverseSided: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );\n\n }\n },\n renderSingleSided: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( WebGLRenderTarget.prototype, {\n\n wrapS: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );\n return this.texture.wrapS;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );\n this.texture.wrapS = value;\n\n }\n },\n wrapT: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );\n return this.texture.wrapT;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );\n this.texture.wrapT = value;\n\n }\n },\n magFilter: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );\n return this.texture.magFilter;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );\n this.texture.magFilter = value;\n\n }\n },\n minFilter: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );\n return this.texture.minFilter;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );\n this.texture.minFilter = value;\n\n }\n },\n anisotropy: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );\n return this.texture.anisotropy;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );\n this.texture.anisotropy = value;\n\n }\n },\n offset: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );\n return this.texture.offset;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );\n this.texture.offset = value;\n\n }\n },\n repeat: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );\n return this.texture.repeat;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );\n this.texture.repeat = value;\n\n }\n },\n format: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );\n return this.texture.format;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );\n this.texture.format = value;\n\n }\n },\n type: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );\n return this.texture.type;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );\n this.texture.type = value;\n\n }\n },\n generateMipmaps: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );\n return this.texture.generateMipmaps;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );\n this.texture.generateMipmaps = value;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( WebVRManager.prototype, {\n\n standing: {\n set: function ( /* value */ ) {\n\n console.warn( 'THREE.WebVRManager: .standing has been removed.' );\n\n }\n }\n\n } );\n\n //\n\n Audio.prototype.load = function ( file ) {\n\n console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' );\n var scope = this;\n var audioLoader = new AudioLoader();\n audioLoader.load( file, function ( buffer ) {\n\n scope.setBuffer( buffer );\n\n } );\n return this;\n\n };\n\n AudioAnalyser.prototype.getData = function () {\n\n console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' );\n return this.getFrequencyData();\n\n };\n\n //\n\n CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) {\n\n console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' );\n return this.update( renderer, scene );\n\n };\n\n //\n\n var GeometryUtils = {\n\n merge: function ( geometry1, geometry2, materialIndexOffset ) {\n\n console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );\n var matrix;\n\n if ( geometry2.isMesh ) {\n\n geometry2.matrixAutoUpdate && geometry2.updateMatrix();\n\n matrix = geometry2.matrix;\n geometry2 = geometry2.geometry;\n\n }\n\n geometry1.merge( geometry2, matrix, materialIndexOffset );\n\n },\n\n center: function ( geometry ) {\n\n console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );\n return geometry.center();\n\n }\n\n };\n\n var ImageUtils = {\n\n crossOrigin: undefined,\n\n loadTexture: function ( url, mapping, onLoad, onError ) {\n\n console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' );\n\n var loader = new TextureLoader();\n loader.setCrossOrigin( this.crossOrigin );\n\n var texture = loader.load( url, onLoad, undefined, onError );\n\n if ( mapping ) texture.mapping = mapping;\n\n return texture;\n\n },\n\n loadTextureCube: function ( urls, mapping, onLoad, onError ) {\n\n console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' );\n\n var loader = new CubeTextureLoader();\n loader.setCrossOrigin( this.crossOrigin );\n\n var texture = loader.load( urls, onLoad, undefined, onError );\n\n if ( mapping ) texture.mapping = mapping;\n\n return texture;\n\n },\n\n loadCompressedTexture: function () {\n\n console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' );\n\n },\n\n loadCompressedTextureCube: function () {\n\n console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' );\n\n }\n\n };\n\n //\n\n function Projector() {\n\n console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );\n\n this.projectVector = function ( vector, camera ) {\n\n console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );\n vector.project( camera );\n\n };\n\n this.unprojectVector = function ( vector, camera ) {\n\n console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );\n vector.unproject( camera );\n\n };\n\n this.pickingRay = function () {\n\n console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );\n\n };\n\n }\n\n //\n\n function CanvasRenderer() {\n\n console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );\n\n this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n this.clear = function () {};\n this.render = function () {};\n this.setClearColor = function () {};\n this.setSize = function () {};\n\n }\n\n //\n\n var SceneUtils = {\n\n createMultiMaterialObject: function ( /* geometry, materials */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n },\n\n detach: function ( /* child, parent, scene */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n },\n\n attach: function ( /* child, scene, parent */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n }\n\n };\n\n //\n\n function LensFlare() {\n\n console.error( 'THREE.LensFlare has been moved to /examples/js/objects/Lensflare.js' );\n\n }\n\n exports.WebGLRenderTargetCube = WebGLRenderTargetCube;\n exports.WebGLRenderTarget = WebGLRenderTarget;\n exports.WebGLRenderer = WebGLRenderer;\n exports.ShaderLib = ShaderLib;\n exports.UniformsLib = UniformsLib;\n exports.UniformsUtils = UniformsUtils;\n exports.ShaderChunk = ShaderChunk;\n exports.FogExp2 = FogExp2;\n exports.Fog = Fog;\n exports.Scene = Scene;\n exports.Sprite = Sprite;\n exports.LOD = LOD;\n exports.SkinnedMesh = SkinnedMesh;\n exports.Skeleton = Skeleton;\n exports.Bone = Bone;\n exports.Mesh = Mesh;\n exports.LineSegments = LineSegments;\n exports.LineLoop = LineLoop;\n exports.Line = Line;\n exports.Points = Points;\n exports.Group = Group;\n exports.VideoTexture = VideoTexture;\n exports.DataTexture = DataTexture;\n exports.CompressedTexture = CompressedTexture;\n exports.CubeTexture = CubeTexture;\n exports.CanvasTexture = CanvasTexture;\n exports.DepthTexture = DepthTexture;\n exports.Texture = Texture;\n exports.CompressedTextureLoader = CompressedTextureLoader;\n exports.DataTextureLoader = DataTextureLoader;\n exports.CubeTextureLoader = CubeTextureLoader;\n exports.TextureLoader = TextureLoader;\n exports.ObjectLoader = ObjectLoader;\n exports.MaterialLoader = MaterialLoader;\n exports.BufferGeometryLoader = BufferGeometryLoader;\n exports.DefaultLoadingManager = DefaultLoadingManager;\n exports.LoadingManager = LoadingManager;\n exports.JSONLoader = JSONLoader;\n exports.ImageLoader = ImageLoader;\n exports.ImageBitmapLoader = ImageBitmapLoader;\n exports.FontLoader = FontLoader;\n exports.FileLoader = FileLoader;\n exports.Loader = Loader;\n exports.LoaderUtils = LoaderUtils;\n exports.Cache = Cache;\n exports.AudioLoader = AudioLoader;\n exports.SpotLightShadow = SpotLightShadow;\n exports.SpotLight = SpotLight;\n exports.PointLight = PointLight;\n exports.RectAreaLight = RectAreaLight;\n exports.HemisphereLight = HemisphereLight;\n exports.DirectionalLightShadow = DirectionalLightShadow;\n exports.DirectionalLight = DirectionalLight;\n exports.AmbientLight = AmbientLight;\n exports.LightShadow = LightShadow;\n exports.Light = Light;\n exports.StereoCamera = StereoCamera;\n exports.PerspectiveCamera = PerspectiveCamera;\n exports.OrthographicCamera = OrthographicCamera;\n exports.CubeCamera = CubeCamera;\n exports.ArrayCamera = ArrayCamera;\n exports.Camera = Camera;\n exports.AudioListener = AudioListener;\n exports.PositionalAudio = PositionalAudio;\n exports.AudioContext = AudioContext;\n exports.AudioAnalyser = AudioAnalyser;\n exports.Audio = Audio;\n exports.VectorKeyframeTrack = VectorKeyframeTrack;\n exports.StringKeyframeTrack = StringKeyframeTrack;\n exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack;\n exports.NumberKeyframeTrack = NumberKeyframeTrack;\n exports.ColorKeyframeTrack = ColorKeyframeTrack;\n exports.BooleanKeyframeTrack = BooleanKeyframeTrack;\n exports.PropertyMixer = PropertyMixer;\n exports.PropertyBinding = PropertyBinding;\n exports.KeyframeTrack = KeyframeTrack;\n exports.AnimationUtils = AnimationUtils;\n exports.AnimationObjectGroup = AnimationObjectGroup;\n exports.AnimationMixer = AnimationMixer;\n exports.AnimationClip = AnimationClip;\n exports.Uniform = Uniform;\n exports.InstancedBufferGeometry = InstancedBufferGeometry;\n exports.BufferGeometry = BufferGeometry;\n exports.Geometry = Geometry;\n exports.InterleavedBufferAttribute = InterleavedBufferAttribute;\n exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer;\n exports.InterleavedBuffer = InterleavedBuffer;\n exports.InstancedBufferAttribute = InstancedBufferAttribute;\n exports.Face3 = Face3;\n exports.Object3D = Object3D;\n exports.Raycaster = Raycaster;\n exports.Layers = Layers;\n exports.EventDispatcher = EventDispatcher;\n exports.Clock = Clock;\n exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant;\n exports.LinearInterpolant = LinearInterpolant;\n exports.DiscreteInterpolant = DiscreteInterpolant;\n exports.CubicInterpolant = CubicInterpolant;\n exports.Interpolant = Interpolant;\n exports.Triangle = Triangle;\n exports.Math = _Math;\n exports.Spherical = Spherical;\n exports.Cylindrical = Cylindrical;\n exports.Plane = Plane;\n exports.Frustum = Frustum;\n exports.Sphere = Sphere;\n exports.Ray = Ray;\n exports.Matrix4 = Matrix4;\n exports.Matrix3 = Matrix3;\n exports.Box3 = Box3;\n exports.Box2 = Box2;\n exports.Line3 = Line3;\n exports.Euler = Euler;\n exports.Vector4 = Vector4;\n exports.Vector3 = Vector3;\n exports.Vector2 = Vector2;\n exports.Quaternion = Quaternion;\n exports.Color = Color;\n exports.ImmediateRenderObject = ImmediateRenderObject;\n exports.VertexNormalsHelper = VertexNormalsHelper;\n exports.SpotLightHelper = SpotLightHelper;\n exports.SkeletonHelper = SkeletonHelper;\n exports.PointLightHelper = PointLightHelper;\n exports.RectAreaLightHelper = RectAreaLightHelper;\n exports.HemisphereLightHelper = HemisphereLightHelper;\n exports.GridHelper = GridHelper;\n exports.PolarGridHelper = PolarGridHelper;\n exports.FaceNormalsHelper = FaceNormalsHelper;\n exports.DirectionalLightHelper = DirectionalLightHelper;\n exports.CameraHelper = CameraHelper;\n exports.BoxHelper = BoxHelper;\n exports.Box3Helper = Box3Helper;\n exports.PlaneHelper = PlaneHelper;\n exports.ArrowHelper = ArrowHelper;\n exports.AxesHelper = AxesHelper;\n exports.Shape = Shape;\n exports.Path = Path;\n exports.ShapePath = ShapePath;\n exports.Font = Font;\n exports.CurvePath = CurvePath;\n exports.Curve = Curve;\n exports.ShapeUtils = ShapeUtils;\n exports.WebGLUtils = WebGLUtils;\n exports.WireframeGeometry = WireframeGeometry;\n exports.ParametricGeometry = ParametricGeometry;\n exports.ParametricBufferGeometry = ParametricBufferGeometry;\n exports.TetrahedronGeometry = TetrahedronGeometry;\n exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry;\n exports.OctahedronGeometry = OctahedronGeometry;\n exports.OctahedronBufferGeometry = OctahedronBufferGeometry;\n exports.IcosahedronGeometry = IcosahedronGeometry;\n exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry;\n exports.DodecahedronGeometry = DodecahedronGeometry;\n exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry;\n exports.PolyhedronGeometry = PolyhedronGeometry;\n exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry;\n exports.TubeGeometry = TubeGeometry;\n exports.TubeBufferGeometry = TubeBufferGeometry;\n exports.TorusKnotGeometry = TorusKnotGeometry;\n exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry;\n exports.TorusGeometry = TorusGeometry;\n exports.TorusBufferGeometry = TorusBufferGeometry;\n exports.TextGeometry = TextGeometry;\n exports.TextBufferGeometry = TextBufferGeometry;\n exports.SphereGeometry = SphereGeometry;\n exports.SphereBufferGeometry = SphereBufferGeometry;\n exports.RingGeometry = RingGeometry;\n exports.RingBufferGeometry = RingBufferGeometry;\n exports.PlaneGeometry = PlaneGeometry;\n exports.PlaneBufferGeometry = PlaneBufferGeometry;\n exports.LatheGeometry = LatheGeometry;\n exports.LatheBufferGeometry = LatheBufferGeometry;\n exports.ShapeGeometry = ShapeGeometry;\n exports.ShapeBufferGeometry = ShapeBufferGeometry;\n exports.ExtrudeGeometry = ExtrudeGeometry;\n exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry;\n exports.EdgesGeometry = EdgesGeometry;\n exports.ConeGeometry = ConeGeometry;\n exports.ConeBufferGeometry = ConeBufferGeometry;\n exports.CylinderGeometry = CylinderGeometry;\n exports.CylinderBufferGeometry = CylinderBufferGeometry;\n exports.CircleGeometry = CircleGeometry;\n exports.CircleBufferGeometry = CircleBufferGeometry;\n exports.BoxGeometry = BoxGeometry;\n exports.BoxBufferGeometry = BoxBufferGeometry;\n exports.ShadowMaterial = ShadowMaterial;\n exports.SpriteMaterial = SpriteMaterial;\n exports.RawShaderMaterial = RawShaderMaterial;\n exports.ShaderMaterial = ShaderMaterial;\n exports.PointsMaterial = PointsMaterial;\n exports.MeshPhysicalMaterial = MeshPhysicalMaterial;\n exports.MeshStandardMaterial = MeshStandardMaterial;\n exports.MeshPhongMaterial = MeshPhongMaterial;\n exports.MeshToonMaterial = MeshToonMaterial;\n exports.MeshNormalMaterial = MeshNormalMaterial;\n exports.MeshLambertMaterial = MeshLambertMaterial;\n exports.MeshDepthMaterial = MeshDepthMaterial;\n exports.MeshDistanceMaterial = MeshDistanceMaterial;\n exports.MeshBasicMaterial = MeshBasicMaterial;\n exports.LineDashedMaterial = LineDashedMaterial;\n exports.LineBasicMaterial = LineBasicMaterial;\n exports.Material = Material;\n exports.Float64BufferAttribute = Float64BufferAttribute;\n exports.Float32BufferAttribute = Float32BufferAttribute;\n exports.Uint32BufferAttribute = Uint32BufferAttribute;\n exports.Int32BufferAttribute = Int32BufferAttribute;\n exports.Uint16BufferAttribute = Uint16BufferAttribute;\n exports.Int16BufferAttribute = Int16BufferAttribute;\n exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute;\n exports.Uint8BufferAttribute = Uint8BufferAttribute;\n exports.Int8BufferAttribute = Int8BufferAttribute;\n exports.BufferAttribute = BufferAttribute;\n exports.ArcCurve = ArcCurve;\n exports.CatmullRomCurve3 = CatmullRomCurve3;\n exports.CubicBezierCurve = CubicBezierCurve;\n exports.CubicBezierCurve3 = CubicBezierCurve3;\n exports.EllipseCurve = EllipseCurve;\n exports.LineCurve = LineCurve;\n exports.LineCurve3 = LineCurve3;\n exports.QuadraticBezierCurve = QuadraticBezierCurve;\n exports.QuadraticBezierCurve3 = QuadraticBezierCurve3;\n exports.SplineCurve = SplineCurve;\n exports.REVISION = REVISION;\n exports.MOUSE = MOUSE;\n exports.CullFaceNone = CullFaceNone;\n exports.CullFaceBack = CullFaceBack;\n exports.CullFaceFront = CullFaceFront;\n exports.CullFaceFrontBack = CullFaceFrontBack;\n exports.FrontFaceDirectionCW = FrontFaceDirectionCW;\n exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW;\n exports.BasicShadowMap = BasicShadowMap;\n exports.PCFShadowMap = PCFShadowMap;\n exports.PCFSoftShadowMap = PCFSoftShadowMap;\n exports.FrontSide = FrontSide;\n exports.BackSide = BackSide;\n exports.DoubleSide = DoubleSide;\n exports.FlatShading = FlatShading;\n exports.SmoothShading = SmoothShading;\n exports.NoColors = NoColors;\n exports.FaceColors = FaceColors;\n exports.VertexColors = VertexColors;\n exports.NoBlending = NoBlending;\n exports.NormalBlending = NormalBlending;\n exports.AdditiveBlending = AdditiveBlending;\n exports.SubtractiveBlending = SubtractiveBlending;\n exports.MultiplyBlending = MultiplyBlending;\n exports.CustomBlending = CustomBlending;\n exports.AddEquation = AddEquation;\n exports.SubtractEquation = SubtractEquation;\n exports.ReverseSubtractEquation = ReverseSubtractEquation;\n exports.MinEquation = MinEquation;\n exports.MaxEquation = MaxEquation;\n exports.ZeroFactor = ZeroFactor;\n exports.OneFactor = OneFactor;\n exports.SrcColorFactor = SrcColorFactor;\n exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor;\n exports.SrcAlphaFactor = SrcAlphaFactor;\n exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor;\n exports.DstAlphaFactor = DstAlphaFactor;\n exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor;\n exports.DstColorFactor = DstColorFactor;\n exports.OneMinusDstColorFactor = OneMinusDstColorFactor;\n exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor;\n exports.NeverDepth = NeverDepth;\n exports.AlwaysDepth = AlwaysDepth;\n exports.LessDepth = LessDepth;\n exports.LessEqualDepth = LessEqualDepth;\n exports.EqualDepth = EqualDepth;\n exports.GreaterEqualDepth = GreaterEqualDepth;\n exports.GreaterDepth = GreaterDepth;\n exports.NotEqualDepth = NotEqualDepth;\n exports.MultiplyOperation = MultiplyOperation;\n exports.MixOperation = MixOperation;\n exports.AddOperation = AddOperation;\n exports.NoToneMapping = NoToneMapping;\n exports.LinearToneMapping = LinearToneMapping;\n exports.ReinhardToneMapping = ReinhardToneMapping;\n exports.Uncharted2ToneMapping = Uncharted2ToneMapping;\n exports.CineonToneMapping = CineonToneMapping;\n exports.UVMapping = UVMapping;\n exports.CubeReflectionMapping = CubeReflectionMapping;\n exports.CubeRefractionMapping = CubeRefractionMapping;\n exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping;\n exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping;\n exports.SphericalReflectionMapping = SphericalReflectionMapping;\n exports.CubeUVReflectionMapping = CubeUVReflectionMapping;\n exports.CubeUVRefractionMapping = CubeUVRefractionMapping;\n exports.RepeatWrapping = RepeatWrapping;\n exports.ClampToEdgeWrapping = ClampToEdgeWrapping;\n exports.MirroredRepeatWrapping = MirroredRepeatWrapping;\n exports.NearestFilter = NearestFilter;\n exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter;\n exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter;\n exports.LinearFilter = LinearFilter;\n exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter;\n exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter;\n exports.UnsignedByteType = UnsignedByteType;\n exports.ByteType = ByteType;\n exports.ShortType = ShortType;\n exports.UnsignedShortType = UnsignedShortType;\n exports.IntType = IntType;\n exports.UnsignedIntType = UnsignedIntType;\n exports.FloatType = FloatType;\n exports.HalfFloatType = HalfFloatType;\n exports.UnsignedShort4444Type = UnsignedShort4444Type;\n exports.UnsignedShort5551Type = UnsignedShort5551Type;\n exports.UnsignedShort565Type = UnsignedShort565Type;\n exports.UnsignedInt248Type = UnsignedInt248Type;\n exports.AlphaFormat = AlphaFormat;\n exports.RGBFormat = RGBFormat;\n exports.RGBAFormat = RGBAFormat;\n exports.LuminanceFormat = LuminanceFormat;\n exports.LuminanceAlphaFormat = LuminanceAlphaFormat;\n exports.RGBEFormat = RGBEFormat;\n exports.DepthFormat = DepthFormat;\n exports.DepthStencilFormat = DepthStencilFormat;\n exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format;\n exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format;\n exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format;\n exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format;\n exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format;\n exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format;\n exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format;\n exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format;\n exports.RGB_ETC1_Format = RGB_ETC1_Format;\n exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format;\n exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format;\n exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format;\n exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format;\n exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format;\n exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format;\n exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format;\n exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format;\n exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format;\n exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format;\n exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format;\n exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format;\n exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format;\n exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format;\n exports.LoopOnce = LoopOnce;\n exports.LoopRepeat = LoopRepeat;\n exports.LoopPingPong = LoopPingPong;\n exports.InterpolateDiscrete = InterpolateDiscrete;\n exports.InterpolateLinear = InterpolateLinear;\n exports.InterpolateSmooth = InterpolateSmooth;\n exports.ZeroCurvatureEnding = ZeroCurvatureEnding;\n exports.ZeroSlopeEnding = ZeroSlopeEnding;\n exports.WrapAroundEnding = WrapAroundEnding;\n exports.TrianglesDrawMode = TrianglesDrawMode;\n exports.TriangleStripDrawMode = TriangleStripDrawMode;\n exports.TriangleFanDrawMode = TriangleFanDrawMode;\n exports.LinearEncoding = LinearEncoding;\n exports.sRGBEncoding = sRGBEncoding;\n exports.GammaEncoding = GammaEncoding;\n exports.RGBEEncoding = RGBEEncoding;\n exports.LogLuvEncoding = LogLuvEncoding;\n exports.RGBM7Encoding = RGBM7Encoding;\n exports.RGBM16Encoding = RGBM16Encoding;\n exports.RGBDEncoding = RGBDEncoding;\n exports.BasicDepthPacking = BasicDepthPacking;\n exports.RGBADepthPacking = RGBADepthPacking;\n exports.CubeGeometry = BoxGeometry;\n exports.Face4 = Face4;\n exports.LineStrip = LineStrip;\n exports.LinePieces = LinePieces;\n exports.MeshFaceMaterial = MeshFaceMaterial;\n exports.MultiMaterial = MultiMaterial;\n exports.PointCloud = PointCloud;\n exports.Particle = Particle;\n exports.ParticleSystem = ParticleSystem;\n exports.PointCloudMaterial = PointCloudMaterial;\n exports.ParticleBasicMaterial = ParticleBasicMaterial;\n exports.ParticleSystemMaterial = ParticleSystemMaterial;\n exports.Vertex = Vertex;\n exports.DynamicBufferAttribute = DynamicBufferAttribute;\n exports.Int8Attribute = Int8Attribute;\n exports.Uint8Attribute = Uint8Attribute;\n exports.Uint8ClampedAttribute = Uint8ClampedAttribute;\n exports.Int16Attribute = Int16Attribute;\n exports.Uint16Attribute = Uint16Attribute;\n exports.Int32Attribute = Int32Attribute;\n exports.Uint32Attribute = Uint32Attribute;\n exports.Float32Attribute = Float32Attribute;\n exports.Float64Attribute = Float64Attribute;\n exports.ClosedSplineCurve3 = ClosedSplineCurve3;\n exports.SplineCurve3 = SplineCurve3;\n exports.Spline = Spline;\n exports.AxisHelper = AxisHelper;\n exports.BoundingBoxHelper = BoundingBoxHelper;\n exports.EdgesHelper = EdgesHelper;\n exports.WireframeHelper = WireframeHelper;\n exports.XHRLoader = XHRLoader;\n exports.BinaryTextureLoader = BinaryTextureLoader;\n exports.GeometryUtils = GeometryUtils;\n exports.ImageUtils = ImageUtils;\n exports.Projector = Projector;\n exports.CanvasRenderer = CanvasRenderer;\n exports.SceneUtils = SceneUtils;\n exports.LensFlare = LensFlare;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n`\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1455.1229188846069","left":"2165.293772061664","inputs":{},"outputs":{}},"0.5881562772156042":{"definition":"//\n// view path\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view path'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n path:{type:'',\n event:function(evt){\n mod.path = evt.detail.path\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n mod.depth = evt.detail.depth\n show_path_info()\n show_path()\n outputs.path.event()\n }},\n response:{type:'three.js',\n event:function(evt){\n var script = document.createElement('script')\n //script.type = 'text/javascript'\n script.onload = init_window\n script.src = 'data:text/javascript,' + encodeURIComponent(evt.detail)\n mod.div.appendChild(script)\n //init_window(evt.detail)\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.path\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n mods.output(mod,'path',cmd)\n }},\n request:{type:'three.js',\n event:function(arg){\n mods.output(mod,'request',arg)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name: ')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mmtext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(in)')\n div.appendChild(text)\n mod.intext = text\n //\n // view\n // \n div.appendChild(document.createElement('br')) \n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('view')\n span.appendChild(text)\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n open_view_window()\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// show_path_info\n//\nfunction show_path_info() {\n mod.nametext.nodeValue = 'name: '+mod.name\n var width = (25.4*mod.width/mod.dpi).toFixed(3)\n var height = (25.4*mod.height/mod.dpi).toFixed(3)\n var depth = (25.4*mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.mmtext.nodeValue = width+' x '+height+' (mm)'\n else\n mod.mmtext.nodeValue = width+' x '+height+' x '+depth+' (mm)'\n var width = (mod.width/mod.dpi).toFixed(3)\n var height = (mod.height/mod.dpi).toFixed(3)\n var depth = (mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.intext.nodeValue = width+' x '+height+' (in)'\n else\n mod.intext.nodeValue = width+' x '+height+' x '+depth+' (in)'\n mods.fit(mod.div)\n }\n//\n// show_path\n//\nfunction show_path() {\n var scene = mod.scene\n var camera = mod.camera\n var renderer = mod.renderer\n //\n // check if view window open\n //\n if (mod.win == undefined) {\n open_view_window()\n return\n }\n //\n // check for path\n //\n if (mod.path == undefined)\n return\n //\n // clear scene, leave camera\n //\n var length = scene.children.length\n for (var c = (length-1); c > 1; --c) {\n scene.remove(scene.children[c])\n }\n //\n // fit camera\n //\n mod.thetaxy = 0\n mod.thetaz = 0\n mod.r = mod.height/2\n mod.x0 = mod.width/2\n mod.y0 = mod.height/2\n camera.position.set(mod.x0,mod.y0,mod.r)\n camera.up = new THREE.Vector3(0,1,0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n //\n // draw segments\n //\n var arrow_size = 1+mod.width/200\n var path = mod.path\n for (var segment = 0; segment < path.length; ++segment) {\n if (segment > 0)\n add_arrow(path[segment-1][path[segment-1].length-1],path[segment][0],0xff0000,arrow_size) \n for (var point = 1; point < path[segment].length; ++point) {\n add_arrow(path[segment][point-1],path[segment][point],0x0000ff,arrow_size)\n }\n }\n //\n // add axes\n //\n var length = mod.height/10\n add_arrow([0,0,0],[length,0,0],0xff0000,arrow_size)\n add_arrow([0,0,0],[0,length,0],0x00ff00,arrow_size)\n add_arrow([0,0,0],[0,0,length],0x0000ff,arrow_size)\n //\n // render\n //\n update()\n //\n // add_arrow\n //\n function add_arrow(start,stop,color,size) {\n var origin = new THREE.Vector3().fromArray(start)\n if (mod.depth == undefined)\n origin.z = 0\n var end = new THREE.Vector3().fromArray(stop)\n if (mod.depth == undefined)\n end.z = 0\n var length = new THREE.Vector3().subVectors(end,origin).length()\n if (length <= size) {\n add_line(origin,end,color)\n //length = 1.1*size\n return\n }\n var direction = new THREE.Vector3().subVectors(end,origin).normalize()\n var arrow = new THREE.ArrowHelper(direction,origin,length,color,size,size)\n scene.add(arrow)\n }\n //\n // add_line\n //\n function add_line(start,stop,colorhex) {\n var geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\n var material = new THREE.LineBasicMaterial({color:colorhex})\n var line = new THREE.Line(geometry,material)\n scene.add(line)\n }\n //\n // update\n //\n function update() {\n renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction open_view_window() {\n //\n // open window\n //\n win = window.open('')\n mod.win = win\n //\n // load three.js\n //\n outputs.request.event('three.js')\n }\n//\n// init_window\n//\nfunction init_window() {\n //document.write('<script type=\"text/javascript\">'+arg+'</script>')\n //document.close()\n //\n // close button\n //\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n mod.win.close()\n mod.win = undefined\n })\n mod.win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n mod.win.document.body.appendChild(text)\n //\n // GL container\n //\n mod.win.document.body.appendChild(document.createElement('br')) \n container = mod.win.document.createElement('div')\n container.style.overflow = 'hidden'\n mod.win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = mod.win.innerWidth\n var height = mod.win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n //\n // context_menu\n //\n function context_menu(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n return (false)\n }\n //\n // mouse_down\n //\n function mouse_down(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n mod.button = evt.button\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_up\n //\n function mouse_up(evt) {\n mod.button = undefined\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_move\n //\n function mouse_move(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dx = evt.clientX-mod.x\n var dy = evt.clientY-mod.y\n mod.x = evt.clientX\n mod.y = evt.clientY\n if (mod.button == 0) {\n mod.x0 += \n Math.sin(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/mod.win.innerHeight\n mod.thetaz += dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/mod.win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// old_open_view_window\n//\nfunction old_open_view_window() {\n //\n // globals\n //\n var container,scene,camera,renderer,win,controls\n //\n // open the window\n //\n open_window()\n //\n // open_window\n //\n function open_window() {\n //\n // open window\n //\n win = window.open('')\n mod.win = win\n //\n // load three.js\n //\n var script = document.createElement('script')\n script.type = 'text/javascript'\n script.onload = init_window\n script.src = 'js/three.js/three.min.js'\n mod.div.appendChild(script)\n }\n //\n // init_window\n //\n function init_window() {\n //\n // close button\n //\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n mod.win = undefined\n })\n win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n win.document.body.appendChild(text)\n //\n // GL container\n //\n win.document.body.appendChild(document.createElement('br')) \n container = win.document.createElement('div')\n container.style.overflow = 'hidden'\n win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = win.innerWidth\n var height = win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n }\n //\n // context_menu\n //\n function context_menu(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n return (false)\n }\n //\n // mouse_down\n //\n function mouse_down(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n mod.button = evt.button\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_up\n //\n function mouse_up(evt) {\n mod.button = undefined\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_move\n //\n function mouse_move(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dx = evt.clientX-mod.x\n var dy = evt.clientY-mod.y\n mod.x = evt.clientX\n mod.y = evt.clientY\n if (mod.button == 0) {\n mod.x0 += \n Math.sin(mod.thetaz)*mod.height*dy/win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/win.innerHeight\n mod.thetaz += dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1243.5329774608936","left":"2187.1353144021764","inputs":{},"outputs":{}},"0.726998031175597":{"definition":"//\n// mill raster 2.5D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2019\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mill raster 2.5D'\n//\n// initialization\n//\nvar init = function() {\n mod.dia_in.value = '0.5'\n mod.dia_mm.value = '12.7'\n mod.cut_in.value = '0.25'\n mod.cut_mm.value = '6.35'\n mod.max_in.value = '3'\n mod.max_mm.value = '76.19999999999999'\n mod.number.value = '3'\n mod.stepover.value = '0.5'\n mod.merge.value = '1'\n mod.sort.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n imageInfo:{type:'',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{type:'',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n //\n // calculation in progress, draw and accumulate\n //\n draw_layer(evt.detail)\n accumulate_layer(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value))\n && (evt.detail.length > 0)) {\n //\n // layer detail present and offset not complete\n //\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else if (mod.depthmm < parseFloat(mod.max_mm.value)) {\n //\n // layer loop not complete\n //\n merge_layer()\n accumulate_path()\n clear_layer()\n mod.depthmm += parseFloat(mod.cut_mm.value)\n if (mod.depthmm > parseFloat(mod.max_mm.value)) {\n mod.depthmm = parseFloat(mod.max_mm.value)\n }\n //\n // clear offset\n //\n outputs.offset.event('')\n //\n // set new depth\n //\n outputs.depth.event(mod.depthmm)\n //\n // set new offset\n //\n mod.offsetCount = 0\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else {\n //\n // done, finish and output\n //\n //draw_path(mod.path)\n //draw_connections()\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n outputs.toolpath.event()\n }\n }\n }\n },\n settings:{type:'',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n depth:{type:'',\n event:function(depth){\n mods.output(mod,'depth',{depthmm:depth})\n }\n },\n diameter:{type:'',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{type:'',\n event:function(size){\n mods.output(mod,'offset',size)\n }\n },\n toolpath:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.toolpath\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n cmd.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\n mods.output(mod,'toolpath',cmd)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // tool diameter\n //\n div.appendChild(document.createTextNode('tool diameter'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.dia_in.value = parseFloat(mod.dia_mm.value)/25.4\n })\n div.appendChild(input)\n mod.dia_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.dia_mm.value = parseFloat(mod.dia_in.value)*25.4\n })\n div.appendChild(input)\n mod.dia_in = input\n div.appendChild(document.createElement('br'))\n //\n // cut depth\n //\n div.appendChild(document.createTextNode('cut depth'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.cut_in.value = parseFloat(mod.cut_mm.value)/25.4\n })\n div.appendChild(input)\n mod.cut_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.cut_mm.value = parseFloat(mod.cut_in.value)*25.4\n })\n div.appendChild(input)\n mod.cut_in = input\n div.appendChild(document.createElement('br'))\n //\n // max depth\n //\n div.appendChild(document.createTextNode('max depth'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.max_in.value = parseFloat(mod.max_mm.value)/25.4\n })\n div.appendChild(input)\n mod.max_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.max_mm.value = parseFloat(mod.max_in.value)*25.4\n })\n div.appendChild(input)\n mod.max_in = input\n div.appendChild(document.createElement('br'))\n //\n // offset number\n //\n div.appendChild(document.createTextNode('offset number: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.number = input\n div.appendChild(document.createTextNode(' (0 = fill)'))\n div.appendChild(document.createElement('br'))\n //\n // offset stepover\n //\n div.appendChild(document.createTextNode('offset stepover: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.stepover = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // direction\n //\n div.appendChild(document.createTextNode('direction: '))\n div.appendChild(document.createTextNode('climb'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'direction'\n input.id = mod.div.id+'climb'\n input.checked = true\n div.appendChild(input)\n mod.climb = input\n div.appendChild(document.createTextNode(' conventional'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'direction'\n input.id = mod.div.id+'conventional'\n div.appendChild(input)\n mod.conventional = input\n div.appendChild(document.createElement('br'))\n //\n // path merge\n //\n div.appendChild(document.createTextNode('path merge: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.merge = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // path order\n //\n div.appendChild(document.createTextNode('path order: '))\n div.appendChild(document.createTextNode('forward'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'order'\n input.id = mod.div.id+'forward'\n input.checked = true\n div.appendChild(input)\n mod.forward = input\n div.appendChild(document.createTextNode(' reverse'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'order'\n input.id = mod.div.id+'reverse'\n div.appendChild(input)\n mod.reverse = input\n div.appendChild(document.createElement('br'))\n //\n // sort distance\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // calculate\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('calculate')\n mod.label = text\n span.appendChild(text)\n mod.labelspan = span\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n mod.label.nodeValue = 'calculating'\n mod.labelspan.style.fontWeight = 'bold'\n outputs.offset.event('') // clear offset\n mod.depthmm = parseFloat(mod.cut_mm.value)\n outputs.depth.event(mod.depthmm) // set depth\n mod.toolpath = [] // start new toolpath\n clear_layer() // clear layer\n outputs.diameter.event()\n outputs.offset.event( // set offset\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' '))\n //\n // view\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n }\n//\n// local functions\n//\n// set_values\n//\nfunction set_values(settings) {\n for (var s in settings) {\n switch(s) {\n case 'tool diameter (in)':\n mod.dia_in.value = settings[s]\n mod.dia_mm.value = parseFloat(mod.dia_in.value)*25.4\n break\n case 'cut depth (in)':\n mod.cut_in.value = settings[s]\n mod.cut_mm.value = parseFloat(mod.cut_in.value)*25.4\n break\n case 'max depth (in)':\n mod.max_in.value = settings[s]\n mod.max_mm.value = parseFloat(mod.max_in.value)*25.4\n break\n case 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_layer\n//\nfunction clear_layer() {\n mod.path = []\n mod.offset = 0.5\n mod.offsetCount = 0\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute(\n 'viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n }\n//\n// accumulate_layer\n// todo: replace inefficient insertion sort\n// todo: move sort out of main thread\n//\nfunction accumulate_layer(path) {\n var forward = mod.forward.checked\n var conventional = mod.conventional.checked\n var sort = mod.sort.checked\n for (var segnew = 0; segnew < path.length; ++segnew) {\n if (conventional)\n path[segnew].reverse()\n if (mod.path.length == 0)\n mod.path.splice(0,0,path[segnew])\n else if (sort) {\n var xnew = path[segnew][0][0]\n var ynew = path[segnew][0][1]\n var dmin = Number.MAX_VALUE\n var segmin = -1\n for (var segold = 0; segold < mod.path.length; ++segold) {\n var xold = mod.path[segold][0][0]\n var yold = mod.path[segold][0][1]\n var dx = xnew-xold\n var dy = ynew-yold\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d < dmin) {\n dmin = d\n segmin = segold\n }\n }\n if (forward)\n mod.path.splice(segmin+1,0,path[segnew])\n else\n mod.path.splice(segmin,0,path[segnew])\n }\n else {\n if (forward)\n mod.path.splice(mod.path.length,0,path[segnew])\n else\n mod.path.splice(0,0,path[segnew])\n }\n }\n }\n//\n// merge_layer\n//\nfunction merge_layer() {\n var dmerge = mod.dpi*parseFloat(mod.merge.value)*parseFloat(mod.dia_in.value)\n var seg = 0\n while (seg < (mod.path.length-1)) {\n var xold = mod.path[seg][mod.path[seg].length-1][0]\n var yold = mod.path[seg][mod.path[seg].length-1][1]\n var xnew = mod.path[seg+1][0][0]\n var ynew = mod.path[seg+1][0][1]\n var dx = xnew-xold\n var dy = ynew-yold\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d < dmerge)\n mod.path.splice(seg,2,mod.path[seg].concat(mod.path[seg+1]))\n else\n seg += 1\n }\n }\n//\n// accumulate_path\n//\nfunction accumulate_path() {\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var newseg = []\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var idepth = -Math.round(mod.dpi*mod.depthmm/25.4)\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n mod.toolpath.splice(mod.toolpath.legnth,0,newseg)\n }\n }\n//\n// add_depth\n//\nfunction add_depth() {\n var cut = parseFloat(mod.cut_in.value)\n var max = parseFloat(mod.max_in.value)\n var newpath = []\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var depth = cut\n if (mod.path[seg][0][0] == mod.path[seg][mod.path[seg].length-1][0]) {\n var newseg = []\n while (depth <= max) {\n var idepth = -Math.round(mod.dpi*depth)\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n if (depth == max)\n break\n depth += cut\n if (depth > max)\n depth = max\n }\n newpath.splice(newpath.length,0,newseg)\n }\n else {\n var newseg = []\n while (depth <= max) {\n var idepth = -Math.round(mod.dpi*depth)\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n newpath.splice(newpath.length,0,newseg)\n newseg = []\n if (depth == max)\n break\n depth += cut\n if (depth > max)\n depth = max\n }\n }\n }\n mod.path = newpath\n mod.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\n }\n//\n// draw_layer\n//\nfunction draw_layer(path) {\n var g = document.getElementById(mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment = 0; segment < path.length; ++segment) {\n if (path[segment].length > 1) {\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS(\n 'http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n 'points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n }\n//\n// draw_connections\n//\nfunction draw_connections() {\n var g = document.getElementById(mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n //\n // loop over segments\n //\n for (var segment = 1; segment < mod.path.length; ++segment) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS(\n 'http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = mod.path[segment-1][mod.path[segment-1].length-1][0]\n var y1 = h-mod.path[segment-1][mod.path[segment-1].length-1][1]-1\n var x2 = mod.path[segment][0][0]\n var y2 = h-mod.path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n 'points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"432.6650099324413","left":"2184.4067477397416","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7562574507163453\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8910984899438215\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5086051394329043\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"response\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"response\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"request\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5086051394329043\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"request\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"depth\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}"]} \ No newline at end of file +{"modules":{"0.47383876715576023":{"definition":"//\n// distance transform \n// assumes thresholded image, with zero intensity exterior\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'distance transform'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n distance_transform()}}}\n//\n// outputs\n//\nvar outputs = {\n distances:{type:'F32',\n event:function(){\n mod.distances.height = mod.input.height\n mod.distances.width = mod.input.width\n mods.output(mod,'distances',mod.distances)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// distance transform function\n//\nfunction distance_transform() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n mod.distances = new Float32Array(evt.data.buffer)\n var imgbuf = new Uint8ClampedArray(h*w*4)\n var dmax = -Number.MAX_VALUE\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n if (mod.distances[(h-1-y)*w+x] > dmax)\n dmax = mod.distances[(h-1-y)*w+x]\n }\n }\n var i\n for (var y = 0; y < h; ++y) {\n for (var x = 0; x < w; ++x) {\n i = 255*mod.distances[(h-1-y)*w+x]/dmax\n imgbuf[(h-1-y)*w*4+x*4+0] = i\n imgbuf[(h-1-y)*w*4+x*4+1] = i\n imgbuf[(h-1-y)*w*4+x*4+2] = i\n imgbuf[(h-1-y)*w*4+x*4+3] = 255\n }\n }\n var imgdata = new ImageData(imgbuf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.distances.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\n//\n// distance transform worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var ny = evt.data.height\n var nx = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Float32Array(nx*ny)\n function distance(g,x,y,i) {\n return ((y-i)*(y-i)+g[i][x]*g[i][x])\n }\n function intersection(g,x,y0,y1) {\n return ((g[y0][x]*g[y0][x]-g[y1][x]*g[y1][x]+y0*y0-y1*y1)/(2.0*(y0-y1)))\n }\n //\n // allocate arrays\n //\n var g = []\n for (var y = 0; y < ny; ++y)\n g[y] = new Uint32Array(nx)\n var h = []\n for (var y = 0; y < ny; ++y)\n h[y] = new Uint32Array(nx)\n var distances = []\n for (var y = 0; y < ny; ++y)\n distances[y] = new Uint32Array(nx)\n var starts = new Uint32Array(ny)\n var minimums = new Uint32Array(ny)\n var d\n //\n // column scan\n // \n for (var y = 0; y < ny; ++y) {\n //\n // right pass\n //\n var closest = -nx\n for (var x = 0; x < nx; ++x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0) {\n g[y][x] = 0\n closest = x\n }\n else\n g[y][x] = (x-closest)\n }\n //\n // left pass\n //\n closest = 2*nx\n for (var x = (nx-1); x >= 0; --x) {\n if (input[(ny-1-y)*nx*4+x*4+0] != 0)\n closest = x\n else {\n d = (closest-x)\n if (d < g[y][x])\n g[y][x] = d\n }\n }\n }\n //\n // row scan\n //\n for (var x = 0; x < nx; ++x) {\n var segment = 0\n starts[0] = 0\n minimums[0] = 0\n //\n // down \n //\n for (var y = 1; y < ny; ++y) {\n while ((segment >= 0) &&\n (distance(g,x,starts[segment],minimums[segment]) > distance(g,x,starts[segment],y)))\n segment -= 1\n if (segment < 0) {\n segment = 0\n minimums[0] = y\n }\n else {\n newstart = 1+intersection(g,x,minimums[segment],y)\n if (newstart < ny) {\n segment += 1\n minimums[segment] = y\n starts[segment] = newstart\n }\n }\n }\n //\n // up \n //\n for (var y = (ny-1); y >= 0; --y) {\n d = Math.sqrt(distance(g,x,y,minimums[segment]))\n output[(ny-1-y)*nx+x] = d\n if (y == starts[segment])\n segment -= 1\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"451.73416597015654","left":"3167.040204340621","inputs":{},"outputs":{}},"0.07944144280928633":{"definition":"//\n// edge detect\n// green = interior, blue = exterior, red = boundary\n// assumes input is thresholded\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'edge detect'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n edge_detect()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:interior, blue:exterior, red:boundary'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// edge detect\n//\nfunction edge_detect() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({worker:worker.toString(),\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var i00,i0m,i0p,im0,ip0,imm,imp,ipm,ipp,row,col\n //\n // find edges - interior\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != i0m) || (i00 != im0) || (i00 != imm)\n || (i00 != imp) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // left and right edges\n //\n for (row = 1; row < (h-1); ++row) {\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm) \n || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // top and bottom edges\n //\n for (col = 1; col < (w-1); ++col) {\n row = h-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != imm) \n || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0m) || (i00 != i0p) || (i00 != ipm) \n || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // corners\n //\n row = 0\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipp = (input[(h-2-row)*w*4+(col+1)*4+0] \n +input[(h-2-row)*w*4+(col+1)*4+1] \n +input[(h-2-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != ip0) || (i00 != ipp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = 0\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n ip0 = (input[(h-2-row)*w*4+col*4+0] \n +input[(h-2-row)*w*4+col*4+1] \n +input[(h-2-row)*w*4+col*4+2])\n ipm = (input[(h-2-row)*w*4+(col-1)*4+0] \n +input[(h-2-row)*w*4+(col-1)*4+1] \n +input[(h-2-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != ip0) || (i00 != ipm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = 0\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0p = (input[(h-1-row)*w*4+(col+1)*4+0] \n +input[(h-1-row)*w*4+(col+1)*4+1] \n +input[(h-1-row)*w*4+(col+1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imp = (input[(h-row)*w*4+(col+1)*4+0] \n +input[(h-row)*w*4+(col+1)*4+1] \n +input[(h-row)*w*4+(col+1)*4+2])\n if ((i00 != i0p) || (i00 != im0) || (i00 != imp)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n row = h-1\n col = w-1\n i00 = (input[(h-1-row)*w*4+col*4+0] \n +input[(h-1-row)*w*4+col*4+1] \n +input[(h-1-row)*w*4+col*4+2])\n i0m = (input[(h-1-row)*w*4+(col-1)*4+0] \n +input[(h-1-row)*w*4+(col-1)*4+1] \n +input[(h-1-row)*w*4+(col-1)*4+2])\n im0 = (input[(h-row)*w*4+col*4+0] \n +input[(h-row)*w*4+col*4+1] \n +input[(h-row)*w*4+col*4+2])\n imm = (input[(h-row)*w*4+(col-1)*4+0] \n +input[(h-row)*w*4+(col-1)*4+1] \n +input[(h-row)*w*4+(col-1)*4+2])\n if ((i00 != i0m) || (i00 != im0) || (i00 != imm)) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else if (i00 == 0) {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"801.7341659701567","left":"3651.040204340621","inputs":{},"outputs":{}},"0.8903773266711255":{"definition":"//\n// orient edges\n// input is green:interior, blue:exterior, red:boundary\n// output is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'orient edges'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n var ctx = mod.display.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n orient_edges()\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // off-screen display canvas\n //\n var canvas = document.createElement('canvas')\n mod.display = canvas\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('red:north, dark red:south'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('green:east, dark green:west'))\n win.document.body.appendChild(document.createElement('br'))\n win.document.body.appendChild(document.createTextNode('blue:start, dark blue:stop'))\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.display,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// orient edges\n//\nfunction orient_edges() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n var disp = new Uint8ClampedArray(evt.data.display)\n var dispdata = new ImageData(disp,w,h)\n var ctx = mod.display.getContext(\"2d\")\n ctx.putImageData(dispdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var w = mod.canvas.width\n var h = mod.canvas.height\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,w,h)\n ctx.drawImage(mod.display,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,\n buffer:mod.input.data.buffer},\n [mod.input.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var input = new Uint8ClampedArray(evt.data.buffer)\n var output = new Uint8ClampedArray(h*w*4)\n var row,col\n var boundary = 0\n var interior = 1\n var exterior = 2\n var alpha = 3\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n //\n // orient body states\n //\n for (row = 1; row < (h-1); ++row) {\n for (col = 1; col < (w-1); ++col) {\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && ((input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && ((input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row-1))*w*4+(col+1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && ((input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n || (input[(h-1-(row+1))*w*4+(col-1)*4+interior] != 0)))\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n }\n }\n }\n //\n // orient edge states\n //\n for (col = 1; col < (w-1); ++col) {\n row = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row+1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= north\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n row = h-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row))*w*4+(col+1)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row-1))*w*4+(col)*4+boundary] != 0)\n && (input[(h-1-(row))*w*4+(col-1)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+northsouth] |= south\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n for (row = 1; row < (h-1); ++row) {\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if ((input[(h-1-(row))*w*4+(col+1)*4+boundary] != 0)\n && (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= east\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n if (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n }\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n if (input[(h-1-(row))*w*4+(col)*4+boundary] != 0) {\n if (input[(h-1-(row-1))*w*4+(col)*4+interior] != 0)\n output[(h-1-row)*w*4+col*4+startstop] |= stop\n if ((input[(h-1-(row))*w*4+(col-1)*4+boundary] != 0)\n && (input[(h-1-(row+1))*w*4+(col)*4+interior] != 0)) {\n output[(h-1-row)*w*4+col*4+eastwest] |= west\n output[(h-1-row)*w*4+col*4+startstop] |= start\n }\n }\n }\n //\n // orient corner states (todo)\n //\n row = 0\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = 0\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = 0\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n row = h-1\n col = w-1\n output[(h-1-row)*w*4+col*4+northsouth] = 0\n output[(h-1-row)*w*4+col*4+eastwest] = 0\n output[(h-1-row)*w*4+col*4+startstop] = 0\n output[(h-1-row)*w*4+col*4+alpha] = 255\n //\n // invert background for display\n //\n var display = new Uint8ClampedArray(h*w*4)\n var r,g,b,i\n for (row = 0; row < h; ++row) {\n for (col = 0; col < w; ++col) {\n r = output[(h-1-row)*w*4+col*4+0]\n g = output[(h-1-row)*w*4+col*4+1]\n b = output[(h-1-row)*w*4+col*4+2]\n i = r+g+b\n if (i != 0) { \n display[(h-1-row)*w*4+col*4+0] = output[(h-1-row)*w*4+col*4+0]\n display[(h-1-row)*w*4+col*4+1] = output[(h-1-row)*w*4+col*4+1]\n display[(h-1-row)*w*4+col*4+2] = output[(h-1-row)*w*4+col*4+2]\n display[(h-1-row)*w*4+col*4+3] = output[(h-1-row)*w*4+col*4+3]\n }\n else {\n display[(h-1-row)*w*4+col*4+0] = 255\n display[(h-1-row)*w*4+col*4+1] = 255\n display[(h-1-row)*w*4+col*4+2] = 255\n display[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n //\n // return output\n //\n self.postMessage({buffer:output.buffer,display:display.buffer},[output.buffer,display.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"895.7341659701567","left":"3205.040204340621","inputs":{},"outputs":{}},"0.6488303557466412":{"definition":"//\n// image threshold\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2015,6\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'image threshold'\n//\n// initialization\n//\nvar init = function() {\n mod.threshold.value = '0.5'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n threshold_image()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // threshold value\n //\n div.appendChild(document.createTextNode('threshold (0-1): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n threshold_image()\n })\n div.appendChild(input)\n mod.threshold = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// threshold image\n//\nfunction threshold_image() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var t = parseFloat(mod.threshold.value)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(mod.input,0,0)\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,threshold:t,\n buffer:img.data.buffer},\n [img.data.buffer])\n }\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var t = evt.data.threshold\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var r,g,b,a,i\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n r = buf[(h-1-row)*w*4+col*4+0] \n g = buf[(h-1-row)*w*4+col*4+1] \n b = buf[(h-1-row)*w*4+col*4+2] \n a = buf[(h-1-row)*w*4+col*4+3] \n i = (r+g+b)/(3*255)\n if (a == 0)\n val = 255\n else if (i > t)\n var val = 255\n else\n var val = 0\n buf[(h-1-row)*w*4+col*4+0] = val\n buf[(h-1-row)*w*4+col*4+1] = val\n buf[(h-1-row)*w*4+col*4+2] = val\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"352.73416597015677","left":"2751.040204340621","inputs":{},"outputs":{}},"0.749132408760488":{"definition":"//\n// vectorize\n// input is red 128:north,64:south, green 128:east,64:west, blue 128:start,64:stop\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the fab modules \n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'vectorize'\n//\n// initialization\n//\nvar init = function() {\n mod.error.value = '1'\n }\n//\n// inputs\n//\nvar inputs = {\n image:{type:'RGBA',\n event:function(evt){\n mod.input = evt.detail\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.input.width\n ctx.canvas.height = mod.input.height \n ctx.putImageData(mod.input,0,0)\n vectorize()\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'array',\n event:function(){\n mods.output(mod,'path',mod.path)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br')) \n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // error value\n //\n div.appendChild(document.createTextNode('vector fit (pixels): '))\n //div.appendChild(document.createElement('br'))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n vectorize()\n })\n div.appendChild(input)\n mod.error = input\n div.appendChild(document.createElement('br'))\n //\n // sort\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n input.checked = true\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // view button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// vectorize\n//\nfunction vectorize() {\n //\n // draw path\n //\n function draw_path(path) {\n window.URL.revokeObjectURL(url)\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute('viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment in path) {\n if (path[segment].length > 1) {\n if (xend != null) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = xend\n var y1 = yend\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS('http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS('http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute('points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n svg.appendChild(g)\n }\n //\n // set up worker\n //\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n webworker.terminate()\n mod.path = evt.data.path\n draw_path(mod.path)\n outputs.path.event()\n })\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.input.height,width:mod.input.width,sort:mod.sort.checked,\n error:parseFloat(mod.error.value),\n buffer:mod.input.data.buffer})\n }\n//\n// vectorize worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var sort = evt.data.sort\n var input = new Uint8ClampedArray(evt.data.buffer)\n var northsouth = 0\n var north = 128\n var south = 64\n var eastwest = 1\n var east = 128\n var west = 64\n var startstop = 2\n var start = 128\n var stop = 64\n var path = []\n //\n // edge follower\n //\n function follow_edges(row,col) {\n if ((input[(h-1-row)*w*4+col*4+northsouth] != 0)\n || (input[(h-1-row)*w*4+col*4+eastwest] != 0)) {\n path[path.length] = [[col,row]]\n while (1) {\n if (input[(h-1-row)*w*4+col*4+northsouth] & north) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~north\n row += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+northsouth] & south) {\n input[(h-1-row)*w*4+col*4+northsouth] =\n input[(h-1-row)*w*4+col*4+northsouth] & ~south\n row -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & east) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~east\n col += 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else if (input[(h-1-row)*w*4+col*4+eastwest] & west) {\n input[(h-1-row)*w*4+col*4+eastwest] =\n input[(h-1-row)*w*4+col*4+eastwest] & ~west\n col -= 1\n path[path.length-1][path[path.length-1].length] = [col,row]\n }\n else\n break\n }\n }\n }\n //\n // follow boundary starts\n //\n for (var row = 1; row < (h-1); ++row) {\n col = 0\n follow_edges(row,col)\n col = w-1\n follow_edges(row,col)\n }\n for (var col = 1; col < (w-1); ++col) {\n row = 0\n follow_edges(row,col)\n row = h-1 \n follow_edges(row,col)\n }\n //\n // follow interior paths\n //\n for (var row = 1; row < (h-1); ++row) {\n for (var col = 1; col < (w-1); ++col) {\n follow_edges(row,col)\n }\n }\n //\n // vectorize path\n //\n var error = evt.data.error\n var vecpath = []\n for (var seg = 0; seg < path.length; ++seg) {\n var x0 = path[seg][0][0]\n var y0 = path[seg][0][1]\n vecpath[vecpath.length] = [[x0,y0]]\n var xsum = x0\n var ysum = y0\n var sum = 1\n for (var pt = 1; pt < path[seg].length; ++pt) {\n var xold = x\n var yold = y\n var x = path[seg][pt][0]\n var y = path[seg][pt][1]\n if (sum == 1) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n var xmean = xsum/sum\n var ymean = ysum/sum\n var dx = xmean-x0\n var dy = ymean-y0\n var d = Math.sqrt(dx*dx+dy*dy)\n var nx = dy/d\n var ny = -dx/d\n var l = Math.abs(nx*(x-x0)+ny*(y-y0))\n if (l < error) {\n xsum += x\n ysum += y\n sum += 1\n }\n else {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [xold,yold]\n x0 = xold\n y0 = yold\n xsum = xold\n ysum = yold\n sum = 1\n }\n }\n if (pt == (path[seg].length-1)) {\n vecpath[vecpath.length-1][vecpath[vecpath.length-1].length] = [x,y]\n }\n }\n }\n //\n // sort path\n //\n if ((vecpath.length > 0) && (sort == true)) {\n var dmin = w*w+h*h\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][0]\n var d = x*x+y*y\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n var sortpath = [vecpath[segmin]]\n vecpath.splice(segmin,1)\n }\n while (vecpath.length > 0) {\n var dmin = w*w+h*h\n var x0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][0]\n var y0 = sortpath[sortpath.length-1][sortpath[sortpath.length-1].length-1][1]\n segmin = null\n for (var seg = 0; seg < vecpath.length; ++seg) {\n var x = vecpath[seg][0][0]\n var y = vecpath[seg][0][1]\n var d = (x-x0)*(x-x0)+(y-y0)*(y-y0)\n if (d < dmin) {\n dmin = d\n segmin = seg\n }\n }\n if (segmin != null) {\n sortpath[sortpath.length] = vecpath[segmin]\n vecpath.splice(segmin,1)\n }\n }\n }\n else if ((vecpath.length > 0) && (sort == false))\n sortpath = vecpath\n else\n sortpath = []\n //\n // return path\n //\n self.postMessage({path:sortpath})\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"800.7341659701567","left":"2786.040204340621","inputs":{},"outputs":{}},"0.4793941661670936":{"definition":"//\n// save file\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'save file'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n file:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.contents = evt.detail.contents\n save_file()\n }}}\n//\n// outputs\n//\nvar outputs = {}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name:')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('size:')\n div.appendChild(text)\n mod.sizetext = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\nfunction save_file() {\n var a = document.createElement('a')\n a.setAttribute('href','data:text/plain;charset=utf-8,'+ \n encodeURIComponent(mod.contents))\n a.setAttribute('download',mod.name)\n a.style.display = 'none'\n document.body.appendChild(a)\n a.click()\n document.body.removeChild(a)\n mod.nametext.nodeValue = 'name: '+mod.name\n mods.fit(mod.div)\n mod.sizetext.nodeValue = 'size: '+mod.contents.length\n mods.fit(mod.div)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1323.2919985029002","left":"1653.9305600175123","inputs":{},"outputs":{}},"0.7562574507163453":{"definition":"//\n// ShopBot\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n\n\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'ShopBot'\n//\n// initialization\n//\nvar init = function() {\n mod.cutspeed.value = '20'\n mod.plungespeed.value = '20'\n mod.jogspeed.value = '75'\n mod.jogheight.value = '5'\n mod.spindlespeed.value = '10000'\n mod.unitsin.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n toolpath:{type:'object',\n event:function(evt){\n mod.name = evt.detail.name\n mod.path = evt.detail.path\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n make_path()\n }}}\n//\n// outputs\n//\nvar outputs = {\n file:{type:'object',\n event:function(str){\n obj = {}\n obj.name = mod.name+\".sbp\"\n obj.contents = str\n mods.output(mod,'file',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // cut speed\n //\n div.appendChild(document.createTextNode('cut speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.cutspeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // plunge speed\n //\n div.appendChild(document.createTextNode('plunge speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.plungespeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // jog speed\n //\n div.appendChild(document.createTextNode('jog speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogspeed = input\n div.appendChild(document.createTextNode(' (mm/s)'))\n div.appendChild(document.createElement('br'))\n //\n // jog height\n //\n div.appendChild(document.createTextNode('jog height: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.jogheight = input\n div.appendChild(document.createTextNode(' (mm)'))\n div.appendChild(document.createElement('br'))\n //\n // spindle speed\n //\n div.appendChild(document.createTextNode('spindle speed: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.spindlespeed = input\n div.appendChild(document.createTextNode(' (RPM)'))\n div.appendChild(document.createElement('br'))\n //\n // file units\n //\n div.appendChild(document.createTextNode('file units:'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsin'\n div.appendChild(input)\n mod.unitsin = input\n div.appendChild(document.createTextNode('in'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'units'\n input.id = mod.div.id+'unitsmm'\n div.appendChild(input)\n mod.unitsmm = input\n div.appendChild(document.createTextNode('mm'))\n }\n//\n// local functions\n//\nfunction make_path() {\n if (mod.unitsin.checked)\n var units = 1\n else\n var units = 25.4\n var dx = units*mod.width/mod.dpi\n var nx = mod.width\n var cut_speed = units*parseFloat(mod.cutspeed.value)/25.4\n var plunge_speed = units*parseFloat(mod.plungespeed.value)/25.4\n var jog_speed = units*parseFloat(mod.jogspeed.value)/25.4\n var jog_height = units*parseFloat(mod.jogheight.value)/25.4\n var spindle_speed = parseFloat(mod.spindlespeed.value)\n var scale = dx/(nx-1)\n str = \"SA\\r\\n\" // set to absolute distances\n str += \"TR,\"+spindle_speed+\",1\\r\\n\" // set spindle speed\n str += \"SO,1,1\\r\\n\" // set output number 1 to on\n str += \"pause 2\\r\\n\" // let spindle come up to speed\n str += \"MS,\"+cut_speed.toFixed(4)+\",\"+plunge_speed.toFixed(4)+\"\\r\\n\" // set xy,z speed\n str += \"JS,\"+jog_speed.toFixed(4)+\",\"+jog_speed.toFixed(4)+\"\\r\\n\" // set jog xy,z speed\n str += \"JZ,\"+jog_height.toFixed(4)+\"\\r\\n\" // move up\n //\n // follow segments\n //\n for (var seg = 0; seg < mod.path.length; ++seg) {\n //\n // move up to starting point\n //\n x = scale*mod.path[seg][0][0]\n y = scale*mod.path[seg][0][1]\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n str += \"J2,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\"\\r\\n\"\n //\n // move down\n //\n z = scale*mod.path[seg][0][2]\n str += \"MZ,\"+z.toFixed(4)+\"\\r\\n\"\n for (var pt = 1; pt < mod.path[seg].length; ++pt) {\n //\n // move to next point\n //\n x = scale*mod.path[seg][pt][0]\n y = scale*mod.path[seg][pt][1]\n z = scale*mod.path[seg][pt][2]\n str += \"M3,\"+x.toFixed(4)+\",\"+y.toFixed(4)+\",\"+z.toFixed(4)+\"\\r\\n\"\n }\n }\n //\n // output file\n //\n str += \"MZ,\"+jog_height.toFixed(4)+\"\\r\\n\"\n outputs.file.event(str)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"955.6926005771381","left":"1652.1709212620551","inputs":{},"outputs":{}},"0.3040697193095865":{"definition":"//\n// mesh rotate\n// \n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mesh rotate'\n//\n// initialization\n//\nvar init = function() {\n mod.rx.value = '0'\n mod.ry.value = '0'\n mod.rz.value = '0'\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = evt.detail\n rotate_mesh()}}}\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // rotation\n //\n div.appendChild(document.createTextNode('rotation (degrees):'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' x: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rx = input\n div.appendChild(document.createTextNode(' y: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.ry = input\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode(' z: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n rotate_mesh()\n })\n div.appendChild(input)\n mod.rz = input\n div.appendChild(document.createTextNode(' (enter)'))\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var text = document.createTextNode('dx:')\n div.appendChild(text)\n mod.dxn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy:')\n div.appendChild(text)\n mod.dyn = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz:')\n div.appendChild(text)\n mod.dzn = text\n div.appendChild(document.createElement('br'))\n }\n//\n// local functions\n//\n// rotate mesh\n//\nfunction rotate_mesh() {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(mod.mesh)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits, rotate, and draw\n //\n var blob = new Blob(['('+rotate_mesh_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // mesh\n //\n mod.mesh = evt.data.mesh\n //\n // output\n //\n outputs.mesh.event(evt.data.rotate)\n })\n //\n // call worker\n //\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n rx:rx,ry:ry,rz:rz,\n image:img.data.buffer,mesh:mod.mesh},\n [img.data.buffer,mod.mesh])\n }\nfunction rotate_mesh_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n //return([x3,y3,z3])\n return({x:x3,y:y3,z:z3})\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var rx = evt.data.rx\n var ry = evt.data.ry\n var rz = evt.data.rz\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0.x > xmax) xmax = p0.x\n if (p0.x < xmin) xmin = p0.x\n if (p0.y > ymax) ymax = p0.y\n if (p0.y < ymin) ymin = p0.y\n if (p0.z > zmax) zmax = p0.z\n if (p0.z < zmin) zmin = p0.z\n var p1 = rotate(x1,y1,z1)\n if (p1.x > xmax) xmax = p1.x\n if (p1.x < xmin) xmin = p1.x\n if (p1.y > ymax) ymax = p1.y\n if (p1.y < ymin) ymin = p1.y\n if (p1.z > zmax) zmax = p1.z\n if (p1.z < zmin) zmin = p1.z\n var p2 = rotate(x2,y2,z2)\n if (p2.x > xmax) xmax = p2.x\n if (p2.x < xmin) xmin = p2.x\n if (p2.y > ymax) ymax = p2.y\n if (p2.y < ymin) ymin = p2.y\n if (p2.z > zmax) zmax = p2.z\n if (p2.z < zmin) zmin = p2.z\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // copy mesh\n //\n var newbuf = evt.data.mesh.slice(0)\n var newview = new DataView(newbuf)\n //\n // copy and draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = (width-1)\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = (height-1)\n }\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n line(p0.x,p0.y,p1.x,p1.y)\n line(p1.x,p1.y,p2.x,p2.y)\n line(p2.x,p2.y,p0.x,p0.y)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1.z,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.x,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.y,endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2.z,endian)\n newoffset += 4\n newoffset += 2\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh,rotate:newbuf},\n [evt.data.image,evt.data.mesh,newbuf])\n self.close()\n })\n }\nfunction old_rotate_mesh() {\n //\n // function to rotate point\n //\n function rotate(x,y,z) {\n var x1 = x\n var y1 = Math.cos(rx)*y-Math.sin(rx)*z\n var z1 = Math.sin(rx)*y+Math.cos(rx)*z\n var x2 = Math.cos(ry)*x1-Math.sin(ry)*z1\n var y2 = y1\n var z2 = Math.sin(ry)*x1+Math.cos(ry)*z1\n var x3 = Math.cos(rz)*x2-Math.sin(rz)*y2\n var y3 = Math.sin(rz)*x2+Math.cos(rz)*y2\n var z3 = z2\n return([x3,y3,z3])\n }\n //\n // get vars\n //\n var view = mod.mesh\n var endian = true\n var triangles = view.getUint32(80,endian)\n mod.triangles = triangles\n var size = 80+4+triangles*(4*12+2)\n var rx = parseFloat(mod.rx.value)*Math.PI/180\n var ry = parseFloat(mod.ry.value)*Math.PI/180\n var rz = parseFloat(mod.rz.value)*Math.PI/180\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n if (p0[0] > xmax) xmax = p0[0]\n if (p0[0] < xmin) xmin = p0[0]\n if (p0[1] > ymax) ymax = p0[1]\n if (p0[1] < ymin) ymin = p0[1]\n if (p0[2] > zmax) zmax = p0[2]\n if (p0[2] < zmin) zmin = p0[2]\n var p1 = rotate(x1,y1,z1)\n if (p1[0] > xmax) xmax = p1[0]\n if (p1[0] < xmin) xmin = p1[0]\n if (p1[1] > ymax) ymax = p1[1]\n if (p1[1] < ymin) ymin = p1[1]\n if (p1[2] > zmax) zmax = p1[2]\n if (p1[2] < zmin) zmin = p1[2]\n var p2 = rotate(x2,y2,z2)\n if (p2[0] > xmax) xmax = p2[0]\n if (p2[0] < xmin) xmin = p2[0]\n if (p2[1] > ymax) ymax = p2[1]\n if (p2[1] < ymin) ymin = p2[1]\n if (p2[2] > zmax) zmax = p2[2]\n if (p2[2] < zmin) zmin = p2[2]\n }\n mod.dx = xmax-xmin\n mod.dy = ymax-ymin\n mod.dz = zmax-zmin\n mod.dxn.nodeValue = 'dx: '+mod.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+mod.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+mod.dz.toFixed(3)\n mod.xmin = xmin\n mod.ymin = ymin\n mod.zmin = zmin\n mod.xmax = xmax\n mod.ymax = ymax\n mod.zmax = zmax\n //\n // copy mesh\n //\n var buf = mod.mesh.buffer.slice(0)\n var newview = new DataView(buf)\n //\n // draw projection and save rotation\n //\n var ctx = mod.meshcanvas.getContext('2d')\n var w = mod.meshcanvas.width\n var h = mod.meshcanvas.height\n ctx.clearRect(0,0,w,h)\n var dx = mod.dx\n var dy = mod.dy\n if (dx > dy) {\n var xo = 0\n var yo = h*.5*(1-dy/dx)\n var xw = w\n var yh = w*dy/dx\n }\n else {\n var xo = w*.5*(1-dx/dy)\n var yo = 0\n var xw = h*dx/dy\n var yh = h\n }\n ctx.beginPath()\n offset = 80+4\n var newoffset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n var p0 = rotate(x0,y0,z0)\n var p1 = rotate(x1,y1,z1)\n var p2 = rotate(x2,y2,z2)\n x0 = xo+xw*(p0[0]-xmin)/dx\n y0 = yo+yh*(ymax-p0[1])/dy\n x1 = xo+xw*(p1[0]-xmin)/dx\n y1 = yo+yh*(ymax-p1[1])/dy\n x2 = xo+xw*(p2[0]-xmin)/dx\n y2 = yo+yh*(ymax-p2[1])/dy\n ctx.moveTo(x0,y0)\n ctx.lineTo(x1,y1)\n ctx.lineTo(x2,y2)\n ctx.lineTo(x0,y0)\n newoffset += 3*4\n newview.setFloat32(newoffset,p0[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p0[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p1[2],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[0],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[1],endian)\n newoffset += 4\n newview.setFloat32(newoffset,p2[2],endian)\n newoffset += 4\n newoffset += 2\n }\n ctx.stroke()\n //\n // generate output\n //\n outputs.mesh.event(buf)\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"454.4270833093813","left":"1179.6997327249337","inputs":{},"outputs":{}},"0.8910984899438215":{"definition":"//\n// read stl\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'read STL'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n }\n//\n// outputs\n//\nvar outputs = {\n mesh:{type:'STL',\n event:function(buffer){\n mods.output(mod,'mesh',buffer)}}\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // file input control\n //\n var file = document.createElement('input')\n file.setAttribute('type','file')\n file.setAttribute('id',div.id+'file_input')\n file.style.position = 'absolute'\n file.style.left = 0\n file.style.top = 0\n file.style.width = 0\n file.style.height = 0\n file.style.opacity = 0\n file.addEventListener('change',function() {\n stl_read_handler()\n })\n div.appendChild(file)\n mod.file = file\n //\n // canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // file select button\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('select stl file'))\n btn.addEventListener('click',function(){\n var file = document.getElementById(div.id+'file_input')\n file.value = null\n file.click()\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // info\n //\n var info = document.createElement('div')\n info.setAttribute('id',div.id+'info')\n var text = document.createTextNode('name: ')\n info.appendChild(text)\n mod.namen = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('size: ')\n info.appendChild(text)\n mod.sizen = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('triangles: ')\n info.appendChild(text)\n mod.trianglesn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dx: ')\n info.appendChild(text)\n mod.dxn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dy: ')\n info.appendChild(text)\n mod.dyn = text\n info.appendChild(document.createElement('br'))\n var text = document.createTextNode('dz: ')\n info.appendChild(text)\n mod.dzn = text\n div.appendChild(info)\n }\n//\n// local functions\n//\n// read handler\n//\nfunction stl_read_handler(event) {\n var file_reader = new FileReader()\n file_reader.onload = stl_load_handler\n input_file = mod.file.files[0]\n file_name = input_file.name\n mod.namen.nodeValue = 'name: '+file_name\n file_reader.readAsArrayBuffer(input_file)\n }\n//\n// load handler\n//\nfunction stl_load_handler(event) {\n //\n // check for binary STL\n //\n var endian = true\n var view = new DataView(event.target.result)\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n if (size != view.byteLength) {\n mod.sizen.nodeValue = 'error: not binary STL'\n mod.trianglesn.nodeValue = ''\n mod.dxn.nodeValue = ''\n mod.dyn.nodeValue = ''\n mod.dzn.nodeValue = ''\n return\n }\n mod.sizen.nodeValue = 'size: '+size\n mod.trianglesn.nodeValue = 'triangles: '+triangles\n //\n // find limits and draw\n //\n var blob = new Blob(['('+draw_limits_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n //\n // worker response\n //\n window.URL.revokeObjectURL(url)\n //\n // size\n //\n mod.dxn.nodeValue = 'dx: '+evt.data.dx.toFixed(3)\n mod.dyn.nodeValue = 'dy: '+evt.data.dy.toFixed(3)\n mod.dzn.nodeValue = 'dz: '+evt.data.dz.toFixed(3)\n //\n // image\n //\n var image = evt.data.image\n var height = mod.canvas.height\n var width = mod.canvas.width\n var buffer = new Uint8ClampedArray(evt.data.image)\n var imgdata = new ImageData(buffer,width,height)\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n //\n // output\n //\n outputs.mesh.event(evt.data.mesh)\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var img = ctx.getImageData(0,0,mod.canvas.width,mod.canvas.height)\n //\n // call worker\n //\n webworker.postMessage({\n height:mod.canvas.height,width:mod.canvas.width,\n image:img.data.buffer,mesh:event.target.result},\n [img.data.buffer,event.target.result])\n }\nfunction draw_limits_worker() {\n self.addEventListener('message',function(evt) {\n //\n // function to draw line\n //\n function line(x0,y0,x1,y1) {\n var ix0 = Math.floor(xo+xw*(x0-xmin)/dx)\n var iy0 = Math.floor(yo+yh*(ymax-y0)/dy)\n var ix1 = Math.floor(xo+xw*(x1-xmin)/dx)\n var iy1 = Math.floor(yo+yh*(ymax-y1)/dy)\n var row,col\n var idx = ix1-ix0\n var idy = iy1-iy0\n if (Math.abs(idy) > Math.abs(idx)) {\n (idy > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (row = row0; row <= row1; ++row) {\n col = Math.floor(col0+(col1-col0)*(row-row0)/(row1-row0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else if ((Math.abs(idx) >= Math.abs(idy)) && (idx != 0)) {\n (idx > 0) ?\n (row0=iy0,col0=ix0,row1=iy1,col1=ix1):\n (row0=iy1,col0=ix1,row1=iy0,col1=ix0)\n for (col = col0; col <= col1; ++col) {\n row = Math.floor(row0+(row1-row0)*(col-col0)/(col1-col0))\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n else {\n row = iy0\n col = ix0\n image[row*width*4+col*4+0] = 0\n image[row*width*4+col*4+1] = 0\n image[row*width*4+col*4+2] = 0\n image[row*width*4+col*4+3] = 255\n }\n }\n //\n // get variables\n //\n var height = evt.data.height\n var width = evt.data.width\n var endian = true\n var image = new Uint8ClampedArray(evt.data.image)\n var view = new DataView(evt.data.mesh)\n var triangles = view.getUint32(80,endian)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n y0 = view.getFloat32(offset,endian)\n offset += 4\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n z0 = view.getFloat32(offset,endian)\n offset += 4\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n x1 = view.getFloat32(offset,endian)\n offset += 4\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n y1 = view.getFloat32(offset,endian)\n offset += 4\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n z1 = view.getFloat32(offset,endian)\n offset += 4\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n x2 = view.getFloat32(offset,endian)\n offset += 4\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n y2 = view.getFloat32(offset,endian)\n offset += 4\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n z2 = view.getFloat32(offset,endian)\n offset += 4\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n offset += 2\n }\n var dx = xmax-xmin\n var dy = ymax-ymin\n var dz = zmax-zmin\n //\n // draw mesh\n //\n if (dx > dy) {\n var xo = 0\n var yo = height*.5*(1-dy/dx)\n var xw = width-1\n var yh = (width-1)*dy/dx\n }\n else {\n var xo = width*.5*(1-dx/dy)\n var yo = 0\n var xw = (height-1)*dx/dy\n var yh = height-1\n }\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)\n offset += 4\n y0 = view.getFloat32(offset,endian)\n offset += 4\n z0 = view.getFloat32(offset,endian)\n offset += 4\n x1 = view.getFloat32(offset,endian)\n offset += 4\n y1 = view.getFloat32(offset,endian)\n offset += 4\n z1 = view.getFloat32(offset,endian)\n offset += 4\n x2 = view.getFloat32(offset,endian)\n offset += 4\n y2 = view.getFloat32(offset,endian)\n offset += 4\n z2 = view.getFloat32(offset,endian)\n offset += 4\n offset += 2\n line(x0,y0,x1,y1)\n line(x1,y1,x2,y2)\n line(x2,y2,x0,y0)\n }\n //\n // return results and close\n //\n self.postMessage({\n dx:dx,dy:dy,dz:dz,\n image:evt.data.image,mesh:evt.data.mesh},[evt.data.image,evt.data.mesh])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"292.47576938638093","left":"773.912070549265","inputs":{},"outputs":{}},"0.20905178335446428":{"definition":"//\n// mesh slice raster\n// \n// todo\n// include slice plane triangles\n// scale perturbation to resolution\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2018\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mesh slice raster'\n//\n// initialization\n//\nvar init = function() {\n mod.mmunits.value = '25.4'\n mod.inunits.value = '1'\n mod.depth.value = '5'\n mod.width.value = '1000'\n mod.border.value = '0'\n mod.delta = 1e-6\n }\n//\n// inputs\n//\nvar inputs = {\n mesh:{type:'STL',\n event:function(evt){\n mod.mesh = new DataView(evt.detail)\n find_limits_slice()}},\n settings:{type:'',\n event:function(evt){\n for (var p in evt.detail)\n if (p == 'depthmm') {\n mod.depth.value = evt.detail[p]\n /parseFloat(mod.mmunits.value)\n }\n find_limits_slice()}}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)\n }},\n imageInfo:{type:'',\n event:function(){\n var obj = {}\n obj.name = \"mesh slice raster\"\n obj.width = mod.img.width\n obj.height = mod.img.height\n obj.dpi = mod.img.width/(mod.dx*parseFloat(mod.inunits.value))\n mods.output(mod,'imageInfo',obj)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen slice canvas\n //\n div.appendChild(document.createTextNode(' '))\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.slicecanvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // mesh units\n //\n div.appendChild(document.createTextNode('mesh units: (enter)'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.inunits.value = parseFloat(mod.mmunits.value)/25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.mmunits = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n mod.mmunits.value = parseFloat(mod.inunits.value)*25.4\n find_limits_slice()\n })\n div.appendChild(input)\n mod.inunits = input\n //\n // mesh size\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mesh size:'))\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (units)')\n div.appendChild(text)\n mod.meshsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (mm)')\n div.appendChild(text)\n mod.mmsize = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('XxYxZ (in)')\n div.appendChild(text)\n mod.insize = text\n //\n // slice depth\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice Z depth: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.depth = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice border \n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice border: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.border = input\n div.appendChild(document.createTextNode(' (units)'))\n //\n // slice width\n //\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('slice width: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n find_limits_slice()\n })\n div.appendChild(input)\n mod.width = input\n div.appendChild(document.createTextNode(' (pixels)'))\n //\n // view slice\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view slice'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// find limits then slice\n//\nfunction find_limits_slice() {\n var blob = new Blob(['('+limits_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n mod.triangles = evt.data.triangles\n mod.xmin = evt.data.xmin\n mod.xmax = evt.data.xmax\n mod.ymin = evt.data.ymin\n mod.ymax = evt.data.ymax\n mod.zmin = evt.data.zmin\n mod.zmax = evt.data.zmax\n mod.dx = mod.xmax-mod.xmin\n mod.dy = mod.ymax-mod.ymin\n mod.dz = mod.zmax-mod.zmin\n mod.meshsize.nodeValue = \n mod.dx.toFixed(3)+' x '+\n mod.dy.toFixed(3)+' x '+\n mod.dz.toFixed(3)+' (units)'\n var mm = parseFloat(mod.mmunits.value)\n mod.mmsize.nodeValue = \n (mod.dx*mm).toFixed(3)+' x '+\n (mod.dy*mm).toFixed(3)+' x '+\n (mod.dz*mm).toFixed(3)+' (mm)'\n var inches = parseFloat(mod.inunits.value)\n mod.insize.nodeValue = \n (mod.dx*inches).toFixed(3)+' x '+\n (mod.dy*inches).toFixed(3)+' x '+\n (mod.dz*inches).toFixed(3)+' (in)'\n mods.fit(mod.div)\n slice_mesh()\n })\n var border = parseFloat(mod.border.value)\n webworker.postMessage({\n mesh:mod.mesh,\n border:border,delta:mod.delta})\n }\nfunction limits_worker() {\n self.addEventListener('message',function(evt) {\n var view = evt.data.mesh\n var depth = evt.data.depth\n var border = evt.data.border\n var delta = evt.data.delta // perturb to remove degeneracies\n //\n // get vars\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // find limits\n //\n var offset = 80+4\n var x0,x1,x2,y0,y1,y2,z0,z1,z2\n var xmin = Number.MAX_VALUE\n var xmax = -Number.MAX_VALUE\n var ymin = Number.MAX_VALUE\n var ymax = -Number.MAX_VALUE\n var zmin = Number.MAX_VALUE\n var zmax = -Number.MAX_VALUE\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n offset += 2\n if (x0 > xmax) xmax = x0\n if (x0 < xmin) xmin = x0\n if (y0 > ymax) ymax = y0\n if (y0 < ymin) ymin = y0\n if (z0 > zmax) zmax = z0\n if (z0 < zmin) zmin = z0\n if (x1 > xmax) xmax = x1\n if (x1 < xmin) xmin = x1\n if (y1 > ymax) ymax = y1\n if (y1 < ymin) ymin = y1\n if (z1 > zmax) zmax = z1\n if (z1 < zmin) zmin = z1\n if (x2 > xmax) xmax = x2\n if (x2 < xmin) xmin = x2\n if (y2 > ymax) ymax = y2\n if (y2 < ymin) ymin = y2\n if (z2 > zmax) zmax = z2\n if (z2 < zmin) zmin = z2\n }\n xmin -= border\n xmax += border\n ymin -= border\n ymax += border\n //\n // return\n //\n self.postMessage({triangles:triangles,\n xmin:xmin,xmax:xmax,ymin:ymin,ymax:ymax,\n zmin:zmin,zmax:zmax})\n self.close()\n })\n }\n//\n// slice mesh\n// \nfunction slice_mesh() {\n var blob = new Blob(['('+slice_worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.img.height\n var w = mod.img.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.slicecanvas.height*.5*(1-h/w)\n var wd = mod.slicecanvas.width\n var hd = mod.slicecanvas.width*h/w\n }\n else {\n var x0 = mod.slicecanvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.slicecanvas.height*w/h\n var hd = mod.slicecanvas.height\n }\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n outputs.image.event()\n outputs.imageInfo.event()\n })\n var ctx = mod.slicecanvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.slicecanvas.width,mod.slicecanvas.height)\n var depth = parseFloat(mod.depth.value)\n mod.img.width = parseInt(mod.width.value)\n mod.img.height = Math.round(mod.img.width*mod.dy/mod.dx)\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n webworker.postMessage({\n height:mod.img.height,width:mod.img.width,depth:depth,\n imgbuffer:img.data.buffer,mesh:mod.mesh,\n xmin:mod.xmin,xmax:mod.xmax,\n ymin:mod.ymin,ymax:mod.ymax,\n zmin:mod.zmin,zmax:mod.zmax,\n delta:mod.delta},\n [img.data.buffer])\n }\nfunction slice_worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var depth = evt.data.depth\n var view = evt.data.mesh\n var delta = evt.data.delta // perturb to remove degeneracies\n var xmin = evt.data.xmin\n var xmax = evt.data.xmax\n var ymin = evt.data.ymin\n var ymax = evt.data.ymax\n var zmin = evt.data.zmin\n var zmax = evt.data.zmax\n var buf = new Uint8ClampedArray(evt.data.imgbuffer)\n //\n // get vars from buffer\n //\n var endian = true\n var triangles = view.getUint32(80,endian)\n var size = 80+4+triangles*(4*12+2)\n //\n // initialize slice image\n //\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n buf[(h-1-row)*w*4+col*4+0] = 0\n buf[(h-1-row)*w*4+col*4+1] = 0\n buf[(h-1-row)*w*4+col*4+2] = 0\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // find triangles crossing the slice\n //\n var segs = []\n offset = 80+4\n for (var t = 0; t < triangles; ++t) {\n offset += 3*4\n x0 = view.getFloat32(offset,endian)+delta\n offset += 4\n y0 = view.getFloat32(offset,endian)+delta\n offset += 4\n z0 = view.getFloat32(offset,endian)+delta\n offset += 4\n x1 = view.getFloat32(offset,endian)+delta\n offset += 4\n y1 = view.getFloat32(offset,endian)+delta\n offset += 4\n z1 = view.getFloat32(offset,endian)+delta\n offset += 4\n x2 = view.getFloat32(offset,endian)+delta\n offset += 4\n y2 = view.getFloat32(offset,endian)+delta\n offset += 4\n z2 = view.getFloat32(offset,endian)+delta\n offset += 4\n //\n // assemble vertices\n //\n offset += 2\n var v = [[x0,y0,z0],[x1,y1,z1],[x2,y2,z2]]\n //\n // sort z\n //\n v.sort(function(a,b) {\n if (a[2] < b[2])\n return -1\n else if (a[2] > b[2])\n return 1\n else\n return 0\n })\n //\n // check for crossings\n //\n if ((v[0][2] < (zmax-depth)) && (v[2][2] > (zmax-depth))) {\n //\n // crossing found, check for side and save\n //\n if (v[1][2] < (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[2][0]+(v[1][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n var y1 = v[2][1]+(v[1][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[1][2])\n }\n else if (v[1][2] >= (zmax-depth)) {\n var x0 = v[2][0]+(v[0][0]-v[2][0])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var y0 = v[2][1]+(v[0][1]-v[2][1])\n *(v[2][2]-(zmax-depth))/(v[2][2]-v[0][2])\n var x1 = v[1][0]+(v[0][0]-v[1][0])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n var y1 = v[1][1]+(v[0][1]-v[1][1])\n *(v[1][2]-(zmax-depth))/(v[1][2]-v[0][2])\n }\n if (y0 < y1)\n segs.push({x0:x0,y0:y0,x1:x1,y1:y1})\n else\n segs.push({x0:x1,y0:y1,x1:x0,y1:y0})\n }\n }\n //\n // fill interior\n //\n for (var row = 0; row < h; ++row) {\n var y = ymin+(ymax-ymin)*row/(h-1)\n rowsegs = segs.filter(p => ((p.y0 <= y) && (p.y1 >= y)))\n var xs = rowsegs.map(p =>\n (p.x0+(p.x1-p.x0)*(y-p.y0)/(p.y1-p.y0)))\n xs.sort((a,b) => (a-b))\n for (var col = 0; col < w; ++col) {\n var x = xmin+(xmax-xmin)*col/(w-1)\n var index = xs.findIndex((p) => (p >= x))\n if (index == -1)\n var i = 0\n else\n var i = 255*(index%2)\n buf[(h-1-row)*w*4+col*4+0] = i\n buf[(h-1-row)*w*4+col*4+1] = i\n buf[(h-1-row)*w*4+col*4+2] = i\n buf[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n //\n // output the slice\n //\n self.postMessage({buffer:buf.buffer},[buf.buffer])\n self.close()\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"300.6601951095721","left":"1614.31672208591","inputs":{},"outputs":{}},"0.5791854769792683":{"definition":"//\n// offset\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2019\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'offset'\n//\n// initialization\n//\nvar init = function() {\n mod.offset.value = '50'\n mod.distances = ''\n }\n//\n// inputs\n//\nvar inputs = {\n distances:{type:'F32',\n event:function(evt){\n mod.distances = evt.detail\n var h = mod.distances.height\n var w = mod.distances.width\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.height = mod.distances.height \n ctx.canvas.width = mod.distances.width\n if (mod.offset.value != '')\n offset()\n }},\n offset:{type:'number',\n event:function(evt){\n mod.offset.value = evt.detail\n if ((mod.offset.value != '') && (mod.distances != ''))\n offset()\n else\n mod.distances = ''\n }}}\n//\n// outputs\n//\nvar outputs = {\n image:{type:'RGBA',\n event:function(){\n var ctx = mod.img.getContext(\"2d\")\n var img = ctx.getImageData(0,0,mod.img.width,mod.img.height)\n mods.output(mod,'image',img)}}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // on-screen drawing canvas\n //\n var canvas = document.createElement('canvas')\n canvas.width = mods.ui.canvas\n canvas.height = mods.ui.canvas\n canvas.style.backgroundColor = 'rgb(255,255,255)'\n div.appendChild(canvas)\n mod.canvas = canvas\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n //\n // offset value\n //\n div.appendChild(document.createTextNode('offset (pixels): '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('change',function(){\n offset()\n })\n div.appendChild(input)\n mod.offset = input\n //\n // view button\n //\n div.appendChild(document.createElement('br'))\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var canvas = document.createElement('canvas')\n canvas.width = mod.img.width\n canvas.height = mod.img.height\n win.document.body.appendChild(canvas)\n var ctx = canvas.getContext(\"2d\")\n ctx.drawImage(mod.img,0,0)\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// offset\n//\nfunction offset() {\n var blob = new Blob(['('+worker.toString()+'())'])\n var url = window.URL.createObjectURL(blob)\n var webworker = new Worker(url)\n webworker.addEventListener('message',function(evt) {\n window.URL.revokeObjectURL(url)\n var h = mod.distances.height\n var w = mod.distances.width\n var buf = new Uint8ClampedArray(evt.data.buffer)\n var imgdata = new ImageData(buf,w,h)\n var ctx = mod.img.getContext(\"2d\")\n ctx.putImageData(imgdata,0,0)\n if (w > h) {\n var x0 = 0\n var y0 = mod.canvas.height*.5*(1-h/w)\n var wd = mod.canvas.width\n var hd = mod.canvas.width*h/w\n }\n else {\n var x0 = mod.canvas.width*.5*(1-w/h)\n var y0 = 0\n var wd = mod.canvas.height*w/h\n var hd = mod.canvas.height\n }\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n ctx.drawImage(mod.img,x0,y0,wd,hd)\n webworker.terminate()\n outputs.image.event()\n })\n var ctx = mod.canvas.getContext(\"2d\")\n ctx.clearRect(0,0,mod.canvas.width,mod.canvas.height)\n var offset = parseFloat(mod.offset.value)\n webworker.postMessage({\n height:mod.distances.height,width:mod.distances.width,\n offset:offset,buffer:mod.distances.buffer})\n }\n//\n// offset worker\n//\nfunction worker() {\n self.addEventListener('message',function(evt) {\n var h = evt.data.height\n var w = evt.data.width\n var offset = evt.data.offset\n var input = new Float32Array(evt.data.buffer)\n var output = new Uint8ClampedArray(4*h*w)\n for (var row = 0; row < h; ++row) {\n for (var col = 0; col < w; ++col) {\n if (input[(h-1-row)*w+col] <= offset) {\n output[(h-1-row)*w*4+col*4+0] = 255\n output[(h-1-row)*w*4+col*4+1] = 255\n output[(h-1-row)*w*4+col*4+2] = 255\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n else {\n output[(h-1-row)*w*4+col*4+0] = 0\n output[(h-1-row)*w*4+col*4+1] = 0\n output[(h-1-row)*w*4+col*4+2] = 0\n output[(h-1-row)*w*4+col*4+3] = 255\n }\n }\n }\n self.postMessage({buffer:output.buffer},[output.buffer])\n })\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"311.1716402604384","left":"3724.071002771963","inputs":{},"outputs":{}},"0.5086051394329043":{"definition":"//\n// three.js library\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2018\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'three.js library'\n//\n// initialization\n//\nvar init = function() {\n mod.library = {}\n store_library()\n list_library()\n }\n//\n// inputs\n//\nvar inputs = {\n store:{type:'object',\n event:function(evt) {\n store_object(evt.detail)\n }},\n request:{type:'key',\n event:function(evt) {\n outputs.response.event(mod.library[evt.detail])\n }}}\n//\n// outputs\n//\nvar outputs = {\n response:{type:'property',\n event:function(prop){\n mods.output(mod,'response',prop)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n div.appendChild(document.createTextNode('objects (length):'))\n div.appendChild(document.createElement('br'))\n var text = document.createElement('textarea')\n text.setAttribute('rows',mods.ui.rows)\n text.setAttribute('cols',mods.ui.cols)\n text.addEventListener('input',function(evt) {\n format_string()\n })\n div.appendChild(text)\n mod.objects = text\n }\n//\n// local functions\n//\nfunction store_object(obj) {\n for (key in obj) {\n mod.library[key] = obj[key]\n }\n list_library()\n }\nfunction list_library() {\n var str = ''\n for (key in mod.library) {\n str += key+' ('+mod.library[key].length+')\\n'\n }\n mod.objects.value = str\n }\nfunction store_library() {\n mod.library['three.js'] = \n`\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n typeof define === 'function' && define.amd ? define(['exports'], factory) :\n (factory((global.THREE = {})));\n}(this, (function (exports) { 'use strict';\n\n // Polyfills\n\n if ( Number.EPSILON === undefined ) {\n\n Number.EPSILON = Math.pow( 2, - 52 );\n\n }\n\n if ( Number.isInteger === undefined ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger\n\n Number.isInteger = function ( value ) {\n\n return typeof value === 'number' && isFinite( value ) && Math.floor( value ) === value;\n\n };\n\n }\n\n //\n\n if ( Math.sign === undefined ) {\n\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign\n\n Math.sign = function ( x ) {\n\n return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x;\n\n };\n\n }\n\n if ( 'name' in Function.prototype === false ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name\n\n Object.defineProperty( Function.prototype, 'name', {\n\n get: function () {\n\n return this.toString().match( /^\\s*function\\s*([^\\(\\s]*)/ )[ 1 ];\n\n }\n\n } );\n\n }\n\n if ( Object.assign === undefined ) {\n\n // Missing in IE\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign\n\n ( function () {\n\n Object.assign = function ( target ) {\n\n if ( target === undefined || target === null ) {\n\n throw new TypeError( 'Cannot convert undefined or null to object' );\n\n }\n\n var output = Object( target );\n\n for ( var index = 1; index < arguments.length; index ++ ) {\n\n var source = arguments[ index ];\n\n if ( source !== undefined && source !== null ) {\n\n for ( var nextKey in source ) {\n\n if ( Object.prototype.hasOwnProperty.call( source, nextKey ) ) {\n\n output[ nextKey ] = source[ nextKey ];\n\n }\n\n }\n\n }\n\n }\n\n return output;\n\n };\n\n } )();\n\n }\n\n /**\n * https://github.com/mrdoob/eventdispatcher.js/\n */\n\n function EventDispatcher() {}\n\n Object.assign( EventDispatcher.prototype, {\n\n addEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) this._listeners = {};\n\n var listeners = this._listeners;\n\n if ( listeners[ type ] === undefined ) {\n\n listeners[ type ] = [];\n\n }\n\n if ( listeners[ type ].indexOf( listener ) === - 1 ) {\n\n listeners[ type ].push( listener );\n\n }\n\n },\n\n hasEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) return false;\n\n var listeners = this._listeners;\n\n return listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1;\n\n },\n\n removeEventListener: function ( type, listener ) {\n\n if ( this._listeners === undefined ) return;\n\n var listeners = this._listeners;\n var listenerArray = listeners[ type ];\n\n if ( listenerArray !== undefined ) {\n\n var index = listenerArray.indexOf( listener );\n\n if ( index !== - 1 ) {\n\n listenerArray.splice( index, 1 );\n\n }\n\n }\n\n },\n\n dispatchEvent: function ( event ) {\n\n if ( this._listeners === undefined ) return;\n\n var listeners = this._listeners;\n var listenerArray = listeners[ event.type ];\n\n if ( listenerArray !== undefined ) {\n\n event.target = this;\n\n var array = listenerArray.slice( 0 );\n\n for ( var i = 0, l = array.length; i < l; i ++ ) {\n\n array[ i ].call( this, event );\n\n }\n\n }\n\n }\n\n } );\n\n var REVISION = '91';\n var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 };\n var CullFaceNone = 0;\n var CullFaceBack = 1;\n var CullFaceFront = 2;\n var CullFaceFrontBack = 3;\n var FrontFaceDirectionCW = 0;\n var FrontFaceDirectionCCW = 1;\n var BasicShadowMap = 0;\n var PCFShadowMap = 1;\n var PCFSoftShadowMap = 2;\n var FrontSide = 0;\n var BackSide = 1;\n var DoubleSide = 2;\n var FlatShading = 1;\n var SmoothShading = 2;\n var NoColors = 0;\n var FaceColors = 1;\n var VertexColors = 2;\n var NoBlending = 0;\n var NormalBlending = 1;\n var AdditiveBlending = 2;\n var SubtractiveBlending = 3;\n var MultiplyBlending = 4;\n var CustomBlending = 5;\n var AddEquation = 100;\n var SubtractEquation = 101;\n var ReverseSubtractEquation = 102;\n var MinEquation = 103;\n var MaxEquation = 104;\n var ZeroFactor = 200;\n var OneFactor = 201;\n var SrcColorFactor = 202;\n var OneMinusSrcColorFactor = 203;\n var SrcAlphaFactor = 204;\n var OneMinusSrcAlphaFactor = 205;\n var DstAlphaFactor = 206;\n var OneMinusDstAlphaFactor = 207;\n var DstColorFactor = 208;\n var OneMinusDstColorFactor = 209;\n var SrcAlphaSaturateFactor = 210;\n var NeverDepth = 0;\n var AlwaysDepth = 1;\n var LessDepth = 2;\n var LessEqualDepth = 3;\n var EqualDepth = 4;\n var GreaterEqualDepth = 5;\n var GreaterDepth = 6;\n var NotEqualDepth = 7;\n var MultiplyOperation = 0;\n var MixOperation = 1;\n var AddOperation = 2;\n var NoToneMapping = 0;\n var LinearToneMapping = 1;\n var ReinhardToneMapping = 2;\n var Uncharted2ToneMapping = 3;\n var CineonToneMapping = 4;\n var UVMapping = 300;\n var CubeReflectionMapping = 301;\n var CubeRefractionMapping = 302;\n var EquirectangularReflectionMapping = 303;\n var EquirectangularRefractionMapping = 304;\n var SphericalReflectionMapping = 305;\n var CubeUVReflectionMapping = 306;\n var CubeUVRefractionMapping = 307;\n var RepeatWrapping = 1000;\n var ClampToEdgeWrapping = 1001;\n var MirroredRepeatWrapping = 1002;\n var NearestFilter = 1003;\n var NearestMipMapNearestFilter = 1004;\n var NearestMipMapLinearFilter = 1005;\n var LinearFilter = 1006;\n var LinearMipMapNearestFilter = 1007;\n var LinearMipMapLinearFilter = 1008;\n var UnsignedByteType = 1009;\n var ByteType = 1010;\n var ShortType = 1011;\n var UnsignedShortType = 1012;\n var IntType = 1013;\n var UnsignedIntType = 1014;\n var FloatType = 1015;\n var HalfFloatType = 1016;\n var UnsignedShort4444Type = 1017;\n var UnsignedShort5551Type = 1018;\n var UnsignedShort565Type = 1019;\n var UnsignedInt248Type = 1020;\n var AlphaFormat = 1021;\n var RGBFormat = 1022;\n var RGBAFormat = 1023;\n var LuminanceFormat = 1024;\n var LuminanceAlphaFormat = 1025;\n var RGBEFormat = RGBAFormat;\n var DepthFormat = 1026;\n var DepthStencilFormat = 1027;\n var RGB_S3TC_DXT1_Format = 33776;\n var RGBA_S3TC_DXT1_Format = 33777;\n var RGBA_S3TC_DXT3_Format = 33778;\n var RGBA_S3TC_DXT5_Format = 33779;\n var RGB_PVRTC_4BPPV1_Format = 35840;\n var RGB_PVRTC_2BPPV1_Format = 35841;\n var RGBA_PVRTC_4BPPV1_Format = 35842;\n var RGBA_PVRTC_2BPPV1_Format = 35843;\n var RGB_ETC1_Format = 36196;\n var RGBA_ASTC_4x4_Format = 37808;\n var RGBA_ASTC_5x4_Format = 37809;\n var RGBA_ASTC_5x5_Format = 37810;\n var RGBA_ASTC_6x5_Format = 37811;\n var RGBA_ASTC_6x6_Format = 37812;\n var RGBA_ASTC_8x5_Format = 37813;\n var RGBA_ASTC_8x6_Format = 37814;\n var RGBA_ASTC_8x8_Format = 37815;\n var RGBA_ASTC_10x5_Format = 37816;\n var RGBA_ASTC_10x6_Format = 37817;\n var RGBA_ASTC_10x8_Format = 37818;\n var RGBA_ASTC_10x10_Format = 37819;\n var RGBA_ASTC_12x10_Format = 37820;\n var RGBA_ASTC_12x12_Format = 37821;\n var LoopOnce = 2200;\n var LoopRepeat = 2201;\n var LoopPingPong = 2202;\n var InterpolateDiscrete = 2300;\n var InterpolateLinear = 2301;\n var InterpolateSmooth = 2302;\n var ZeroCurvatureEnding = 2400;\n var ZeroSlopeEnding = 2401;\n var WrapAroundEnding = 2402;\n var TrianglesDrawMode = 0;\n var TriangleStripDrawMode = 1;\n var TriangleFanDrawMode = 2;\n var LinearEncoding = 3000;\n var sRGBEncoding = 3001;\n var GammaEncoding = 3007;\n var RGBEEncoding = 3002;\n var LogLuvEncoding = 3003;\n var RGBM7Encoding = 3004;\n var RGBM16Encoding = 3005;\n var RGBDEncoding = 3006;\n var BasicDepthPacking = 3200;\n var RGBADepthPacking = 3201;\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n var _Math = {\n\n DEG2RAD: Math.PI / 180,\n RAD2DEG: 180 / Math.PI,\n\n generateUUID: ( function () {\n\n // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136\n\n var lut = [];\n\n for ( var i = 0; i < 256; i ++ ) {\n\n lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 ).toUpperCase();\n\n }\n\n return function generateUUID() {\n\n var d0 = Math.random() * 0xffffffff | 0;\n var d1 = Math.random() * 0xffffffff | 0;\n var d2 = Math.random() * 0xffffffff | 0;\n var d3 = Math.random() * 0xffffffff | 0;\n return lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +\n lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +\n lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +\n lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];\n\n };\n\n } )(),\n\n clamp: function ( value, min, max ) {\n\n return Math.max( min, Math.min( max, value ) );\n\n },\n\n // compute euclidian modulo of m % n\n // https://en.wikipedia.org/wiki/Modulo_operation\n\n euclideanModulo: function ( n, m ) {\n\n return ( ( n % m ) + m ) % m;\n\n },\n\n // Linear mapping from range <a1, a2> to range <b1, b2>\n\n mapLinear: function ( x, a1, a2, b1, b2 ) {\n\n return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );\n\n },\n\n // https://en.wikipedia.org/wiki/Linear_interpolation\n\n lerp: function ( x, y, t ) {\n\n return ( 1 - t ) * x + t * y;\n\n },\n\n // http://en.wikipedia.org/wiki/Smoothstep\n\n smoothstep: function ( x, min, max ) {\n\n if ( x <= min ) return 0;\n if ( x >= max ) return 1;\n\n x = ( x - min ) / ( max - min );\n\n return x * x * ( 3 - 2 * x );\n\n },\n\n smootherstep: function ( x, min, max ) {\n\n if ( x <= min ) return 0;\n if ( x >= max ) return 1;\n\n x = ( x - min ) / ( max - min );\n\n return x * x * x * ( x * ( x * 6 - 15 ) + 10 );\n\n },\n\n // Random integer from <low, high> interval\n\n randInt: function ( low, high ) {\n\n return low + Math.floor( Math.random() * ( high - low + 1 ) );\n\n },\n\n // Random float from <low, high> interval\n\n randFloat: function ( low, high ) {\n\n return low + Math.random() * ( high - low );\n\n },\n\n // Random float from <-range/2, range/2> interval\n\n randFloatSpread: function ( range ) {\n\n return range * ( 0.5 - Math.random() );\n\n },\n\n degToRad: function ( degrees ) {\n\n return degrees * _Math.DEG2RAD;\n\n },\n\n radToDeg: function ( radians ) {\n\n return radians * _Math.RAD2DEG;\n\n },\n\n isPowerOfTwo: function ( value ) {\n\n return ( value & ( value - 1 ) ) === 0 && value !== 0;\n\n },\n\n ceilPowerOfTwo: function ( value ) {\n\n return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );\n\n },\n\n floorPowerOfTwo: function ( value ) {\n\n return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author philogb / http://blog.thejit.org/\n * @author egraether / http://egraether.com/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n */\n\n function Vector2( x, y ) {\n\n this.x = x || 0;\n this.y = y || 0;\n\n }\n\n Object.defineProperties( Vector2.prototype, {\n\n \"width\": {\n\n get: function () {\n\n return this.x;\n\n },\n\n set: function ( value ) {\n\n this.x = value;\n\n }\n\n },\n\n \"height\": {\n\n get: function () {\n\n return this.y;\n\n },\n\n set: function ( value ) {\n\n this.y = value;\n\n }\n\n }\n\n } );\n\n Object.assign( Vector2.prototype, {\n\n isVector2: true,\n\n set: function ( x, y ) {\n\n this.x = x;\n this.y = y;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n\n return this;\n\n },\n\n multiply: function ( v ) {\n\n this.x *= v.x;\n this.y *= v.y;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n\n return this;\n\n },\n\n divide: function ( v ) {\n\n this.x /= v.x;\n this.y /= v.y;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n applyMatrix3: function ( m ) {\n\n var x = this.x, y = this.y;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ];\n this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ];\n\n return this;\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min = new Vector2();\n var max = new Vector2();\n\n return function clampScalar( minVal, maxVal ) {\n\n min.set( minVal, minVal );\n max.set( maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y;\n\n },\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n angle: function () {\n\n // computes the angle in radians with respect to the positive x-axis\n\n var angle = Math.atan2( this.y, this.x );\n\n if ( angle < 0 ) angle += 2 * Math.PI;\n\n return angle;\n\n },\n\n distanceTo: function ( v ) {\n\n return Math.sqrt( this.distanceToSquared( v ) );\n\n },\n\n distanceToSquared: function ( v ) {\n\n var dx = this.x - v.x, dy = this.y - v.y;\n return dx * dx + dy * dy;\n\n },\n\n manhattanDistanceTo: function ( v ) {\n\n return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector2: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n\n return this;\n\n },\n\n rotateAround: function ( center, angle ) {\n\n var c = Math.cos( angle ), s = Math.sin( angle );\n\n var x = this.x - center.x;\n var y = this.y - center.y;\n\n this.x = x * c - y * s + center.x;\n this.y = x * s + y * c + center.y;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author philogb / http://blog.thejit.org/\n * @author jordi_ros / http://plattsoft.com\n * @author D1plo1d / http://github.com/D1plo1d\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author timknip / http://www.floorplanner.com/\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Matrix4() {\n\n this.elements = [\n\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n ];\n\n if ( arguments.length > 0 ) {\n\n console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' );\n\n }\n\n }\n\n Object.assign( Matrix4.prototype, {\n\n isMatrix4: true,\n\n set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) {\n\n var te = this.elements;\n\n te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14;\n te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24;\n te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34;\n te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44;\n\n return this;\n\n },\n\n identity: function () {\n\n this.set(\n\n 1, 0, 0, 0,\n 0, 1, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n clone: function () {\n\n return new Matrix4().fromArray( this.elements );\n\n },\n\n copy: function ( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ]; te[ 3 ] = me[ 3 ];\n te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ]; te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ];\n te[ 8 ] = me[ 8 ]; te[ 9 ] = me[ 9 ]; te[ 10 ] = me[ 10 ]; te[ 11 ] = me[ 11 ];\n te[ 12 ] = me[ 12 ]; te[ 13 ] = me[ 13 ]; te[ 14 ] = me[ 14 ]; te[ 15 ] = me[ 15 ];\n\n return this;\n\n },\n\n copyPosition: function ( m ) {\n\n var te = this.elements, me = m.elements;\n\n te[ 12 ] = me[ 12 ];\n te[ 13 ] = me[ 13 ];\n te[ 14 ] = me[ 14 ];\n\n return this;\n\n },\n\n extractBasis: function ( xAxis, yAxis, zAxis ) {\n\n xAxis.setFromMatrixColumn( this, 0 );\n yAxis.setFromMatrixColumn( this, 1 );\n zAxis.setFromMatrixColumn( this, 2 );\n\n return this;\n\n },\n\n makeBasis: function ( xAxis, yAxis, zAxis ) {\n\n this.set(\n xAxis.x, yAxis.x, zAxis.x, 0,\n xAxis.y, yAxis.y, zAxis.y, 0,\n xAxis.z, yAxis.z, zAxis.z, 0,\n 0, 0, 0, 1\n );\n\n return this;\n\n },\n\n extractRotation: function () {\n\n var v1 = new Vector3();\n\n return function extractRotation( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n var scaleX = 1 / v1.setFromMatrixColumn( m, 0 ).length();\n var scaleY = 1 / v1.setFromMatrixColumn( m, 1 ).length();\n var scaleZ = 1 / v1.setFromMatrixColumn( m, 2 ).length();\n\n te[ 0 ] = me[ 0 ] * scaleX;\n te[ 1 ] = me[ 1 ] * scaleX;\n te[ 2 ] = me[ 2 ] * scaleX;\n\n te[ 4 ] = me[ 4 ] * scaleY;\n te[ 5 ] = me[ 5 ] * scaleY;\n te[ 6 ] = me[ 6 ] * scaleY;\n\n te[ 8 ] = me[ 8 ] * scaleZ;\n te[ 9 ] = me[ 9 ] * scaleZ;\n te[ 10 ] = me[ 10 ] * scaleZ;\n\n return this;\n\n };\n\n }(),\n\n makeRotationFromEuler: function ( euler ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n console.error( 'THREE.Matrix4: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );\n\n }\n\n var te = this.elements;\n\n var x = euler.x, y = euler.y, z = euler.z;\n var a = Math.cos( x ), b = Math.sin( x );\n var c = Math.cos( y ), d = Math.sin( y );\n var e = Math.cos( z ), f = Math.sin( z );\n\n if ( euler.order === 'XYZ' ) {\n\n var ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n te[ 0 ] = c * e;\n te[ 4 ] = - c * f;\n te[ 8 ] = d;\n\n te[ 1 ] = af + be * d;\n te[ 5 ] = ae - bf * d;\n te[ 9 ] = - b * c;\n\n te[ 2 ] = bf - ae * d;\n te[ 6 ] = be + af * d;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'YXZ' ) {\n\n var ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n te[ 0 ] = ce + df * b;\n te[ 4 ] = de * b - cf;\n te[ 8 ] = a * d;\n\n te[ 1 ] = a * f;\n te[ 5 ] = a * e;\n te[ 9 ] = - b;\n\n te[ 2 ] = cf * b - de;\n te[ 6 ] = df + ce * b;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'ZXY' ) {\n\n var ce = c * e, cf = c * f, de = d * e, df = d * f;\n\n te[ 0 ] = ce - df * b;\n te[ 4 ] = - a * f;\n te[ 8 ] = de + cf * b;\n\n te[ 1 ] = cf + de * b;\n te[ 5 ] = a * e;\n te[ 9 ] = df - ce * b;\n\n te[ 2 ] = - a * d;\n te[ 6 ] = b;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'ZYX' ) {\n\n var ae = a * e, af = a * f, be = b * e, bf = b * f;\n\n te[ 0 ] = c * e;\n te[ 4 ] = be * d - af;\n te[ 8 ] = ae * d + bf;\n\n te[ 1 ] = c * f;\n te[ 5 ] = bf * d + ae;\n te[ 9 ] = af * d - be;\n\n te[ 2 ] = - d;\n te[ 6 ] = b * c;\n te[ 10 ] = a * c;\n\n } else if ( euler.order === 'YZX' ) {\n\n var ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n te[ 0 ] = c * e;\n te[ 4 ] = bd - ac * f;\n te[ 8 ] = bc * f + ad;\n\n te[ 1 ] = f;\n te[ 5 ] = a * e;\n te[ 9 ] = - b * e;\n\n te[ 2 ] = - d * e;\n te[ 6 ] = ad * f + bc;\n te[ 10 ] = ac - bd * f;\n\n } else if ( euler.order === 'XZY' ) {\n\n var ac = a * c, ad = a * d, bc = b * c, bd = b * d;\n\n te[ 0 ] = c * e;\n te[ 4 ] = - f;\n te[ 8 ] = d * e;\n\n te[ 1 ] = ac * f + bd;\n te[ 5 ] = a * e;\n te[ 9 ] = ad * f - bc;\n\n te[ 2 ] = bc * f - ad;\n te[ 6 ] = b * e;\n te[ 10 ] = bd * f + ac;\n\n }\n\n // last column\n te[ 3 ] = 0;\n te[ 7 ] = 0;\n te[ 11 ] = 0;\n\n // bottom row\n te[ 12 ] = 0;\n te[ 13 ] = 0;\n te[ 14 ] = 0;\n te[ 15 ] = 1;\n\n return this;\n\n },\n\n makeRotationFromQuaternion: function ( q ) {\n\n var te = this.elements;\n\n var x = q._x, y = q._y, z = q._z, w = q._w;\n var x2 = x + x, y2 = y + y, z2 = z + z;\n var xx = x * x2, xy = x * y2, xz = x * z2;\n var yy = y * y2, yz = y * z2, zz = z * z2;\n var wx = w * x2, wy = w * y2, wz = w * z2;\n\n te[ 0 ] = 1 - ( yy + zz );\n te[ 4 ] = xy - wz;\n te[ 8 ] = xz + wy;\n\n te[ 1 ] = xy + wz;\n te[ 5 ] = 1 - ( xx + zz );\n te[ 9 ] = yz - wx;\n\n te[ 2 ] = xz - wy;\n te[ 6 ] = yz + wx;\n te[ 10 ] = 1 - ( xx + yy );\n\n // last column\n te[ 3 ] = 0;\n te[ 7 ] = 0;\n te[ 11 ] = 0;\n\n // bottom row\n te[ 12 ] = 0;\n te[ 13 ] = 0;\n te[ 14 ] = 0;\n te[ 15 ] = 1;\n\n return this;\n\n },\n\n lookAt: function () {\n\n var x = new Vector3();\n var y = new Vector3();\n var z = new Vector3();\n\n return function lookAt( eye, target, up ) {\n\n var te = this.elements;\n\n z.subVectors( eye, target );\n\n if ( z.lengthSq() === 0 ) {\n\n // eye and target are in the same position\n\n z.z = 1;\n\n }\n\n z.normalize();\n x.crossVectors( up, z );\n\n if ( x.lengthSq() === 0 ) {\n\n // up and z are parallel\n\n if ( Math.abs( up.z ) === 1 ) {\n\n z.x += 0.0001;\n\n } else {\n\n z.z += 0.0001;\n\n }\n\n z.normalize();\n x.crossVectors( up, z );\n\n }\n\n x.normalize();\n y.crossVectors( z, x );\n\n te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x;\n te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y;\n te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z;\n\n return this;\n\n };\n\n }(),\n\n multiply: function ( m, n ) {\n\n if ( n !== undefined ) {\n\n console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' );\n return this.multiplyMatrices( m, n );\n\n }\n\n return this.multiplyMatrices( this, m );\n\n },\n\n premultiply: function ( m ) {\n\n return this.multiplyMatrices( m, this );\n\n },\n\n multiplyMatrices: function ( a, b ) {\n\n var ae = a.elements;\n var be = b.elements;\n var te = this.elements;\n\n var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ];\n var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ];\n var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ];\n var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ];\n\n var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ];\n var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ];\n var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ];\n var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ];\n\n te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41;\n te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42;\n te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43;\n te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44;\n\n te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41;\n te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42;\n te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43;\n te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44;\n\n te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41;\n te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42;\n te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43;\n te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44;\n\n te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41;\n te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42;\n te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43;\n te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n var te = this.elements;\n\n te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s;\n te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s;\n te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s;\n te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s;\n\n return this;\n\n },\n\n applyToBufferAttribute: function () {\n\n var v1 = new Vector3();\n\n return function applyToBufferAttribute( attribute ) {\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.x = attribute.getX( i );\n v1.y = attribute.getY( i );\n v1.z = attribute.getZ( i );\n\n v1.applyMatrix4( this );\n\n attribute.setXYZ( i, v1.x, v1.y, v1.z );\n\n }\n\n return attribute;\n\n };\n\n }(),\n\n determinant: function () {\n\n var te = this.elements;\n\n var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ];\n var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ];\n var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ];\n var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ];\n\n //TODO: make this more efficient\n //( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm )\n\n return (\n n41 * (\n + n14 * n23 * n32\n - n13 * n24 * n32\n - n14 * n22 * n33\n + n12 * n24 * n33\n + n13 * n22 * n34\n - n12 * n23 * n34\n ) +\n n42 * (\n + n11 * n23 * n34\n - n11 * n24 * n33\n + n14 * n21 * n33\n - n13 * n21 * n34\n + n13 * n24 * n31\n - n14 * n23 * n31\n ) +\n n43 * (\n + n11 * n24 * n32\n - n11 * n22 * n34\n - n14 * n21 * n32\n + n12 * n21 * n34\n + n14 * n22 * n31\n - n12 * n24 * n31\n ) +\n n44 * (\n - n13 * n22 * n31\n - n11 * n23 * n32\n + n11 * n22 * n33\n + n13 * n21 * n32\n - n12 * n21 * n33\n + n12 * n23 * n31\n )\n\n );\n\n },\n\n transpose: function () {\n\n var te = this.elements;\n var tmp;\n\n tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp;\n tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp;\n tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp;\n\n tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp;\n tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp;\n tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp;\n\n return this;\n\n },\n\n setPosition: function ( v ) {\n\n var te = this.elements;\n\n te[ 12 ] = v.x;\n te[ 13 ] = v.y;\n te[ 14 ] = v.z;\n\n return this;\n\n },\n\n getInverse: function ( m, throwOnDegenerate ) {\n\n // based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm\n var te = this.elements,\n me = m.elements,\n\n n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ], n41 = me[ 3 ],\n n12 = me[ 4 ], n22 = me[ 5 ], n32 = me[ 6 ], n42 = me[ 7 ],\n n13 = me[ 8 ], n23 = me[ 9 ], n33 = me[ 10 ], n43 = me[ 11 ],\n n14 = me[ 12 ], n24 = me[ 13 ], n34 = me[ 14 ], n44 = me[ 15 ],\n\n t11 = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44,\n t12 = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44,\n t13 = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44,\n t14 = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34;\n\n var det = n11 * t11 + n21 * t12 + n31 * t13 + n41 * t14;\n\n if ( det === 0 ) {\n\n var msg = \"THREE.Matrix4: .getInverse() can't invert matrix, determinant is 0\";\n\n if ( throwOnDegenerate === true ) {\n\n throw new Error( msg );\n\n } else {\n\n console.warn( msg );\n\n }\n\n return this.identity();\n\n }\n\n var detInv = 1 / det;\n\n te[ 0 ] = t11 * detInv;\n te[ 1 ] = ( n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44 ) * detInv;\n te[ 2 ] = ( n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44 ) * detInv;\n te[ 3 ] = ( n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43 ) * detInv;\n\n te[ 4 ] = t12 * detInv;\n te[ 5 ] = ( n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44 ) * detInv;\n te[ 6 ] = ( n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44 ) * detInv;\n te[ 7 ] = ( n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43 ) * detInv;\n\n te[ 8 ] = t13 * detInv;\n te[ 9 ] = ( n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44 ) * detInv;\n te[ 10 ] = ( n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44 ) * detInv;\n te[ 11 ] = ( n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43 ) * detInv;\n\n te[ 12 ] = t14 * detInv;\n te[ 13 ] = ( n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34 ) * detInv;\n te[ 14 ] = ( n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34 ) * detInv;\n te[ 15 ] = ( n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33 ) * detInv;\n\n return this;\n\n },\n\n scale: function ( v ) {\n\n var te = this.elements;\n var x = v.x, y = v.y, z = v.z;\n\n te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z;\n te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z;\n te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z;\n te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z;\n\n return this;\n\n },\n\n getMaxScaleOnAxis: function () {\n\n var te = this.elements;\n\n var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ];\n var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ];\n var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ];\n\n return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) );\n\n },\n\n makeTranslation: function ( x, y, z ) {\n\n this.set(\n\n 1, 0, 0, x,\n 0, 1, 0, y,\n 0, 0, 1, z,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationX: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n 1, 0, 0, 0,\n 0, c, - s, 0,\n 0, s, c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationY: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n c, 0, s, 0,\n 0, 1, 0, 0,\n - s, 0, c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationZ: function ( theta ) {\n\n var c = Math.cos( theta ), s = Math.sin( theta );\n\n this.set(\n\n c, - s, 0, 0,\n s, c, 0, 0,\n 0, 0, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeRotationAxis: function ( axis, angle ) {\n\n // Based on http://www.gamedev.net/reference/articles/article1199.asp\n\n var c = Math.cos( angle );\n var s = Math.sin( angle );\n var t = 1 - c;\n var x = axis.x, y = axis.y, z = axis.z;\n var tx = t * x, ty = t * y;\n\n this.set(\n\n tx * x + c, tx * y - s * z, tx * z + s * y, 0,\n tx * y + s * z, ty * y + c, ty * z - s * x, 0,\n tx * z - s * y, ty * z + s * x, t * z * z + c, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeScale: function ( x, y, z ) {\n\n this.set(\n\n x, 0, 0, 0,\n 0, y, 0, 0,\n 0, 0, z, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n makeShear: function ( x, y, z ) {\n\n this.set(\n\n 1, y, z, 0,\n x, 1, z, 0,\n x, y, 1, 0,\n 0, 0, 0, 1\n\n );\n\n return this;\n\n },\n\n compose: function ( position, quaternion, scale ) {\n\n this.makeRotationFromQuaternion( quaternion );\n this.scale( scale );\n this.setPosition( position );\n\n return this;\n\n },\n\n decompose: function () {\n\n var vector = new Vector3();\n var matrix = new Matrix4();\n\n return function decompose( position, quaternion, scale ) {\n\n var te = this.elements;\n\n var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length();\n var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length();\n var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length();\n\n // if determine is negative, we need to invert one scale\n var det = this.determinant();\n if ( det < 0 ) sx = - sx;\n\n position.x = te[ 12 ];\n position.y = te[ 13 ];\n position.z = te[ 14 ];\n\n // scale the rotation part\n matrix.copy( this );\n\n var invSX = 1 / sx;\n var invSY = 1 / sy;\n var invSZ = 1 / sz;\n\n matrix.elements[ 0 ] *= invSX;\n matrix.elements[ 1 ] *= invSX;\n matrix.elements[ 2 ] *= invSX;\n\n matrix.elements[ 4 ] *= invSY;\n matrix.elements[ 5 ] *= invSY;\n matrix.elements[ 6 ] *= invSY;\n\n matrix.elements[ 8 ] *= invSZ;\n matrix.elements[ 9 ] *= invSZ;\n matrix.elements[ 10 ] *= invSZ;\n\n quaternion.setFromRotationMatrix( matrix );\n\n scale.x = sx;\n scale.y = sy;\n scale.z = sz;\n\n return this;\n\n };\n\n }(),\n\n makePerspective: function ( left, right, top, bottom, near, far ) {\n\n if ( far === undefined ) {\n\n console.warn( 'THREE.Matrix4: .makePerspective() has been redefined and has a new signature. Please check the docs.' );\n\n }\n\n var te = this.elements;\n var x = 2 * near / ( right - left );\n var y = 2 * near / ( top - bottom );\n\n var a = ( right + left ) / ( right - left );\n var b = ( top + bottom ) / ( top - bottom );\n var c = - ( far + near ) / ( far - near );\n var d = - 2 * far * near / ( far - near );\n\n te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0;\n te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0;\n te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d;\n te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0;\n\n return this;\n\n },\n\n makeOrthographic: function ( left, right, top, bottom, near, far ) {\n\n var te = this.elements;\n var w = 1.0 / ( right - left );\n var h = 1.0 / ( top - bottom );\n var p = 1.0 / ( far - near );\n\n var x = ( right + left ) * w;\n var y = ( top + bottom ) * h;\n var z = ( far + near ) * p;\n\n te[ 0 ] = 2 * w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x;\n te[ 1 ] = 0; te[ 5 ] = 2 * h; te[ 9 ] = 0; te[ 13 ] = - y;\n te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 * p; te[ 14 ] = - z;\n te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1;\n\n return this;\n\n },\n\n equals: function ( matrix ) {\n\n var te = this.elements;\n var me = matrix.elements;\n\n for ( var i = 0; i < 16; i ++ ) {\n\n if ( te[ i ] !== me[ i ] ) return false;\n\n }\n\n return true;\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n for ( var i = 0; i < 16; i ++ ) {\n\n this.elements[ i ] = array[ i + offset ];\n\n }\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n var te = this.elements;\n\n array[ offset ] = te[ 0 ];\n array[ offset + 1 ] = te[ 1 ];\n array[ offset + 2 ] = te[ 2 ];\n array[ offset + 3 ] = te[ 3 ];\n\n array[ offset + 4 ] = te[ 4 ];\n array[ offset + 5 ] = te[ 5 ];\n array[ offset + 6 ] = te[ 6 ];\n array[ offset + 7 ] = te[ 7 ];\n\n array[ offset + 8 ] = te[ 8 ];\n array[ offset + 9 ] = te[ 9 ];\n array[ offset + 10 ] = te[ 10 ];\n array[ offset + 11 ] = te[ 11 ];\n\n array[ offset + 12 ] = te[ 12 ];\n array[ offset + 13 ] = te[ 13 ];\n array[ offset + 14 ] = te[ 14 ];\n array[ offset + 15 ] = te[ 15 ];\n\n return array;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n */\n\n function Quaternion( x, y, z, w ) {\n\n this._x = x || 0;\n this._y = y || 0;\n this._z = z || 0;\n this._w = ( w !== undefined ) ? w : 1;\n\n }\n\n Object.assign( Quaternion, {\n\n slerp: function ( qa, qb, qm, t ) {\n\n return qm.copy( qa ).slerp( qb, t );\n\n },\n\n slerpFlat: function ( dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) {\n\n // fuzz-free, array-based Quaternion SLERP operation\n\n var x0 = src0[ srcOffset0 + 0 ],\n y0 = src0[ srcOffset0 + 1 ],\n z0 = src0[ srcOffset0 + 2 ],\n w0 = src0[ srcOffset0 + 3 ],\n\n x1 = src1[ srcOffset1 + 0 ],\n y1 = src1[ srcOffset1 + 1 ],\n z1 = src1[ srcOffset1 + 2 ],\n w1 = src1[ srcOffset1 + 3 ];\n\n if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) {\n\n var s = 1 - t,\n\n cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1,\n\n dir = ( cos >= 0 ? 1 : - 1 ),\n sqrSin = 1 - cos * cos;\n\n // Skip the Slerp for tiny steps to avoid numeric problems:\n if ( sqrSin > Number.EPSILON ) {\n\n var sin = Math.sqrt( sqrSin ),\n len = Math.atan2( sin, cos * dir );\n\n s = Math.sin( s * len ) / sin;\n t = Math.sin( t * len ) / sin;\n\n }\n\n var tDir = t * dir;\n\n x0 = x0 * s + x1 * tDir;\n y0 = y0 * s + y1 * tDir;\n z0 = z0 * s + z1 * tDir;\n w0 = w0 * s + w1 * tDir;\n\n // Normalize in case we just did a lerp:\n if ( s === 1 - t ) {\n\n var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 );\n\n x0 *= f;\n y0 *= f;\n z0 *= f;\n w0 *= f;\n\n }\n\n }\n\n dst[ dstOffset ] = x0;\n dst[ dstOffset + 1 ] = y0;\n dst[ dstOffset + 2 ] = z0;\n dst[ dstOffset + 3 ] = w0;\n\n }\n\n } );\n\n Object.defineProperties( Quaternion.prototype, {\n\n x: {\n\n get: function () {\n\n return this._x;\n\n },\n\n set: function ( value ) {\n\n this._x = value;\n this.onChangeCallback();\n\n }\n\n },\n\n y: {\n\n get: function () {\n\n return this._y;\n\n },\n\n set: function ( value ) {\n\n this._y = value;\n this.onChangeCallback();\n\n }\n\n },\n\n z: {\n\n get: function () {\n\n return this._z;\n\n },\n\n set: function ( value ) {\n\n this._z = value;\n this.onChangeCallback();\n\n }\n\n },\n\n w: {\n\n get: function () {\n\n return this._w;\n\n },\n\n set: function ( value ) {\n\n this._w = value;\n this.onChangeCallback();\n\n }\n\n }\n\n } );\n\n Object.assign( Quaternion.prototype, {\n\n set: function ( x, y, z, w ) {\n\n this._x = x;\n this._y = y;\n this._z = z;\n this._w = w;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this._x, this._y, this._z, this._w );\n\n },\n\n copy: function ( quaternion ) {\n\n this._x = quaternion.x;\n this._y = quaternion.y;\n this._z = quaternion.z;\n this._w = quaternion.w;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromEuler: function ( euler, update ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n throw new Error( 'THREE.Quaternion: .setFromEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n }\n\n var x = euler._x, y = euler._y, z = euler._z, order = euler.order;\n\n // http://www.mathworks.com/matlabcentral/fileexchange/\n // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/\n // content/SpinCalc.m\n\n var cos = Math.cos;\n var sin = Math.sin;\n\n var c1 = cos( x / 2 );\n var c2 = cos( y / 2 );\n var c3 = cos( z / 2 );\n\n var s1 = sin( x / 2 );\n var s2 = sin( y / 2 );\n var s3 = sin( z / 2 );\n\n if ( order === 'XYZ' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'YXZ' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n } else if ( order === 'ZXY' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'ZYX' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n } else if ( order === 'YZX' ) {\n\n this._x = s1 * c2 * c3 + c1 * s2 * s3;\n this._y = c1 * s2 * c3 + s1 * c2 * s3;\n this._z = c1 * c2 * s3 - s1 * s2 * c3;\n this._w = c1 * c2 * c3 - s1 * s2 * s3;\n\n } else if ( order === 'XZY' ) {\n\n this._x = s1 * c2 * c3 - c1 * s2 * s3;\n this._y = c1 * s2 * c3 - s1 * c2 * s3;\n this._z = c1 * c2 * s3 + s1 * s2 * c3;\n this._w = c1 * c2 * c3 + s1 * s2 * s3;\n\n }\n\n if ( update !== false ) this.onChangeCallback();\n\n return this;\n\n },\n\n setFromAxisAngle: function ( axis, angle ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm\n\n // assumes axis is normalized\n\n var halfAngle = angle / 2, s = Math.sin( halfAngle );\n\n this._x = axis.x * s;\n this._y = axis.y * s;\n this._z = axis.z * s;\n this._w = Math.cos( halfAngle );\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromRotationMatrix: function ( m ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var te = m.elements,\n\n m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],\n\n trace = m11 + m22 + m33,\n s;\n\n if ( trace > 0 ) {\n\n s = 0.5 / Math.sqrt( trace + 1.0 );\n\n this._w = 0.25 / s;\n this._x = ( m32 - m23 ) * s;\n this._y = ( m13 - m31 ) * s;\n this._z = ( m21 - m12 ) * s;\n\n } else if ( m11 > m22 && m11 > m33 ) {\n\n s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );\n\n this._w = ( m32 - m23 ) / s;\n this._x = 0.25 * s;\n this._y = ( m12 + m21 ) / s;\n this._z = ( m13 + m31 ) / s;\n\n } else if ( m22 > m33 ) {\n\n s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );\n\n this._w = ( m13 - m31 ) / s;\n this._x = ( m12 + m21 ) / s;\n this._y = 0.25 * s;\n this._z = ( m23 + m32 ) / s;\n\n } else {\n\n s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );\n\n this._w = ( m21 - m12 ) / s;\n this._x = ( m13 + m31 ) / s;\n this._y = ( m23 + m32 ) / s;\n this._z = 0.25 * s;\n\n }\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromUnitVectors: function () {\n\n // assumes direction vectors vFrom and vTo are normalized\n\n var v1 = new Vector3();\n var r;\n\n var EPS = 0.000001;\n\n return function setFromUnitVectors( vFrom, vTo ) {\n\n if ( v1 === undefined ) v1 = new Vector3();\n\n r = vFrom.dot( vTo ) + 1;\n\n if ( r < EPS ) {\n\n r = 0;\n\n if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {\n\n v1.set( - vFrom.y, vFrom.x, 0 );\n\n } else {\n\n v1.set( 0, - vFrom.z, vFrom.y );\n\n }\n\n } else {\n\n v1.crossVectors( vFrom, vTo );\n\n }\n\n this._x = v1.x;\n this._y = v1.y;\n this._z = v1.z;\n this._w = r;\n\n return this.normalize();\n\n };\n\n }(),\n\n inverse: function () {\n\n // quaternion is assumed to have unit length\n\n return this.conjugate();\n\n },\n\n conjugate: function () {\n\n this._x *= - 1;\n this._y *= - 1;\n this._z *= - 1;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;\n\n },\n\n lengthSq: function () {\n\n return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;\n\n },\n\n length: function () {\n\n return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );\n\n },\n\n normalize: function () {\n\n var l = this.length();\n\n if ( l === 0 ) {\n\n this._x = 0;\n this._y = 0;\n this._z = 0;\n this._w = 1;\n\n } else {\n\n l = 1 / l;\n\n this._x = this._x * l;\n this._y = this._y * l;\n this._z = this._z * l;\n this._w = this._w * l;\n\n }\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n multiply: function ( q, p ) {\n\n if ( p !== undefined ) {\n\n console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );\n return this.multiplyQuaternions( q, p );\n\n }\n\n return this.multiplyQuaternions( this, q );\n\n },\n\n premultiply: function ( q ) {\n\n return this.multiplyQuaternions( q, this );\n\n },\n\n multiplyQuaternions: function ( a, b ) {\n\n // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm\n\n var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;\n var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;\n\n this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;\n this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;\n this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;\n this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n slerp: function ( qb, t ) {\n\n if ( t === 0 ) return this;\n if ( t === 1 ) return this.copy( qb );\n\n var x = this._x, y = this._y, z = this._z, w = this._w;\n\n // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/\n\n var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;\n\n if ( cosHalfTheta < 0 ) {\n\n this._w = - qb._w;\n this._x = - qb._x;\n this._y = - qb._y;\n this._z = - qb._z;\n\n cosHalfTheta = - cosHalfTheta;\n\n } else {\n\n this.copy( qb );\n\n }\n\n if ( cosHalfTheta >= 1.0 ) {\n\n this._w = w;\n this._x = x;\n this._y = y;\n this._z = z;\n\n return this;\n\n }\n\n var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );\n\n if ( Math.abs( sinHalfTheta ) < 0.001 ) {\n\n this._w = 0.5 * ( w + this._w );\n this._x = 0.5 * ( x + this._x );\n this._y = 0.5 * ( y + this._y );\n this._z = 0.5 * ( z + this._z );\n\n return this;\n\n }\n\n var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta );\n var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,\n ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;\n\n this._w = ( w * ratioA + this._w * ratioB );\n this._x = ( x * ratioA + this._x * ratioB );\n this._y = ( y * ratioA + this._y * ratioB );\n this._z = ( z * ratioA + this._z * ratioB );\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n equals: function ( quaternion ) {\n\n return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this._x = array[ offset ];\n this._y = array[ offset + 1 ];\n this._z = array[ offset + 2 ];\n this._w = array[ offset + 3 ];\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this._x;\n array[ offset + 1 ] = this._y;\n array[ offset + 2 ] = this._z;\n array[ offset + 3 ] = this._w;\n\n return array;\n\n },\n\n onChange: function ( callback ) {\n\n this.onChangeCallback = callback;\n\n return this;\n\n },\n\n onChangeCallback: function () {}\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author kile / http://kile.stravaganza.org/\n * @author philogb / http://blog.thejit.org/\n * @author mikael emtinger / http://gomo.se/\n * @author egraether / http://egraether.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Vector3( x, y, z ) {\n\n this.x = x || 0;\n this.y = y || 0;\n this.z = z || 0;\n\n }\n\n Object.assign( Vector3.prototype, {\n\n isVector3: true,\n\n set: function ( x, y, z ) {\n\n this.x = x;\n this.y = y;\n this.z = z;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n this.z = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setZ: function ( z ) {\n\n this.z = z;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n case 2: this.z = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n case 2: return this.z;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y, this.z );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n this.z = v.z;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n this.z += v.z;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n this.z += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n this.z = a.z + b.z;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n this.z += v.z * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n this.z -= v.z;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n this.z -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n this.z = a.z - b.z;\n\n return this;\n\n },\n\n multiply: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );\n return this.multiplyVectors( v, w );\n\n }\n\n this.x *= v.x;\n this.y *= v.y;\n this.z *= v.z;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n this.z *= scalar;\n\n return this;\n\n },\n\n multiplyVectors: function ( a, b ) {\n\n this.x = a.x * b.x;\n this.y = a.y * b.y;\n this.z = a.z * b.z;\n\n return this;\n\n },\n\n applyEuler: function () {\n\n var quaternion = new Quaternion();\n\n return function applyEuler( euler ) {\n\n if ( ! ( euler && euler.isEuler ) ) {\n\n console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' );\n\n }\n\n return this.applyQuaternion( quaternion.setFromEuler( euler ) );\n\n };\n\n }(),\n\n applyAxisAngle: function () {\n\n var quaternion = new Quaternion();\n\n return function applyAxisAngle( axis, angle ) {\n\n return this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );\n\n };\n\n }(),\n\n applyMatrix3: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;\n this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;\n this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;\n\n return this;\n\n },\n\n applyMatrix4: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n var w = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] );\n\n this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * w;\n this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * w;\n this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * w;\n\n return this;\n\n },\n\n applyQuaternion: function ( q ) {\n\n var x = this.x, y = this.y, z = this.z;\n var qx = q.x, qy = q.y, qz = q.z, qw = q.w;\n\n // calculate quat * vector\n\n var ix = qw * x + qy * z - qz * y;\n var iy = qw * y + qz * x - qx * z;\n var iz = qw * z + qx * y - qy * x;\n var iw = - qx * x - qy * y - qz * z;\n\n // calculate result * inverse quat\n\n this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;\n this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;\n this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;\n\n return this;\n\n },\n\n project: function () {\n\n var matrix = new Matrix4();\n\n return function project( camera ) {\n\n matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );\n return this.applyMatrix4( matrix );\n\n };\n\n }(),\n\n unproject: function () {\n\n var matrix = new Matrix4();\n\n return function unproject( camera ) {\n\n matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );\n return this.applyMatrix4( matrix );\n\n };\n\n }(),\n\n transformDirection: function ( m ) {\n\n // input: THREE.Matrix4 affine matrix\n // vector interpreted as a direction\n\n var x = this.x, y = this.y, z = this.z;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;\n this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;\n this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;\n\n return this.normalize();\n\n },\n\n divide: function ( v ) {\n\n this.x /= v.x;\n this.y /= v.y;\n this.z /= v.z;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n this.z = Math.min( this.z, v.z );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n this.z = Math.max( this.z, v.z );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n this.z = Math.max( min.z, Math.min( max.z, this.z ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min = new Vector3();\n var max = new Vector3();\n\n return function clampScalar( minVal, maxVal ) {\n\n min.set( minVal, minVal, minVal );\n max.set( maxVal, maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n this.z = Math.floor( this.z );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n this.z = Math.ceil( this.z );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n this.z = Math.round( this.z );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n this.z = - this.z;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y + this.z * v.z;\n\n },\n\n // TODO lengthSquared?\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y + this.z * this.z;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n this.z += ( v.z - this.z ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n cross: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );\n return this.crossVectors( v, w );\n\n }\n\n return this.crossVectors( this, v );\n\n },\n\n crossVectors: function ( a, b ) {\n\n var ax = a.x, ay = a.y, az = a.z;\n var bx = b.x, by = b.y, bz = b.z;\n\n this.x = ay * bz - az * by;\n this.y = az * bx - ax * bz;\n this.z = ax * by - ay * bx;\n\n return this;\n\n },\n\n projectOnVector: function ( vector ) {\n\n var scalar = vector.dot( this ) / vector.lengthSq();\n\n return this.copy( vector ).multiplyScalar( scalar );\n\n },\n\n projectOnPlane: function () {\n\n var v1 = new Vector3();\n\n return function projectOnPlane( planeNormal ) {\n\n v1.copy( this ).projectOnVector( planeNormal );\n\n return this.sub( v1 );\n\n };\n\n }(),\n\n reflect: function () {\n\n // reflect incident vector off plane orthogonal to normal\n // normal is assumed to have unit length\n\n var v1 = new Vector3();\n\n return function reflect( normal ) {\n\n return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );\n\n };\n\n }(),\n\n angleTo: function ( v ) {\n\n var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) );\n\n // clamp, to handle numerical problems\n\n return Math.acos( _Math.clamp( theta, - 1, 1 ) );\n\n },\n\n distanceTo: function ( v ) {\n\n return Math.sqrt( this.distanceToSquared( v ) );\n\n },\n\n distanceToSquared: function ( v ) {\n\n var dx = this.x - v.x, dy = this.y - v.y, dz = this.z - v.z;\n\n return dx * dx + dy * dy + dz * dz;\n\n },\n\n manhattanDistanceTo: function ( v ) {\n\n return Math.abs( this.x - v.x ) + Math.abs( this.y - v.y ) + Math.abs( this.z - v.z );\n\n },\n\n setFromSpherical: function ( s ) {\n\n var sinPhiRadius = Math.sin( s.phi ) * s.radius;\n\n this.x = sinPhiRadius * Math.sin( s.theta );\n this.y = Math.cos( s.phi ) * s.radius;\n this.z = sinPhiRadius * Math.cos( s.theta );\n\n return this;\n\n },\n\n setFromCylindrical: function ( c ) {\n\n this.x = c.radius * Math.sin( c.theta );\n this.y = c.y;\n this.z = c.radius * Math.cos( c.theta );\n\n return this;\n\n },\n\n setFromMatrixPosition: function ( m ) {\n\n var e = m.elements;\n\n this.x = e[ 12 ];\n this.y = e[ 13 ];\n this.z = e[ 14 ];\n\n return this;\n\n },\n\n setFromMatrixScale: function ( m ) {\n\n var sx = this.setFromMatrixColumn( m, 0 ).length();\n var sy = this.setFromMatrixColumn( m, 1 ).length();\n var sz = this.setFromMatrixColumn( m, 2 ).length();\n\n this.x = sx;\n this.y = sy;\n this.z = sz;\n\n return this;\n\n },\n\n setFromMatrixColumn: function ( m, index ) {\n\n return this.fromArray( m.elements, index * 4 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n this.z = array[ offset + 2 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n array[ offset + 2 ] = this.z;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector3: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n this.z = attribute.getZ( index );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n * @author tschw\n */\n\n function Matrix3() {\n\n this.elements = [\n\n 1, 0, 0,\n 0, 1, 0,\n 0, 0, 1\n\n ];\n\n if ( arguments.length > 0 ) {\n\n console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' );\n\n }\n\n }\n\n Object.assign( Matrix3.prototype, {\n\n isMatrix3: true,\n\n set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) {\n\n var te = this.elements;\n\n te[ 0 ] = n11; te[ 1 ] = n21; te[ 2 ] = n31;\n te[ 3 ] = n12; te[ 4 ] = n22; te[ 5 ] = n32;\n te[ 6 ] = n13; te[ 7 ] = n23; te[ 8 ] = n33;\n\n return this;\n\n },\n\n identity: function () {\n\n this.set(\n\n 1, 0, 0,\n 0, 1, 0,\n 0, 0, 1\n\n );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().fromArray( this.elements );\n\n },\n\n copy: function ( m ) {\n\n var te = this.elements;\n var me = m.elements;\n\n te[ 0 ] = me[ 0 ]; te[ 1 ] = me[ 1 ]; te[ 2 ] = me[ 2 ];\n te[ 3 ] = me[ 3 ]; te[ 4 ] = me[ 4 ]; te[ 5 ] = me[ 5 ];\n te[ 6 ] = me[ 6 ]; te[ 7 ] = me[ 7 ]; te[ 8 ] = me[ 8 ];\n\n return this;\n\n },\n\n setFromMatrix4: function ( m ) {\n\n var me = m.elements;\n\n this.set(\n\n me[ 0 ], me[ 4 ], me[ 8 ],\n me[ 1 ], me[ 5 ], me[ 9 ],\n me[ 2 ], me[ 6 ], me[ 10 ]\n\n );\n\n return this;\n\n },\n\n applyToBufferAttribute: function () {\n\n var v1 = new Vector3();\n\n return function applyToBufferAttribute( attribute ) {\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.x = attribute.getX( i );\n v1.y = attribute.getY( i );\n v1.z = attribute.getZ( i );\n\n v1.applyMatrix3( this );\n\n attribute.setXYZ( i, v1.x, v1.y, v1.z );\n\n }\n\n return attribute;\n\n };\n\n }(),\n\n multiply: function ( m ) {\n\n return this.multiplyMatrices( this, m );\n\n },\n\n premultiply: function ( m ) {\n\n return this.multiplyMatrices( m, this );\n\n },\n\n multiplyMatrices: function ( a, b ) {\n\n var ae = a.elements;\n var be = b.elements;\n var te = this.elements;\n\n var a11 = ae[ 0 ], a12 = ae[ 3 ], a13 = ae[ 6 ];\n var a21 = ae[ 1 ], a22 = ae[ 4 ], a23 = ae[ 7 ];\n var a31 = ae[ 2 ], a32 = ae[ 5 ], a33 = ae[ 8 ];\n\n var b11 = be[ 0 ], b12 = be[ 3 ], b13 = be[ 6 ];\n var b21 = be[ 1 ], b22 = be[ 4 ], b23 = be[ 7 ];\n var b31 = be[ 2 ], b32 = be[ 5 ], b33 = be[ 8 ];\n\n te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31;\n te[ 3 ] = a11 * b12 + a12 * b22 + a13 * b32;\n te[ 6 ] = a11 * b13 + a12 * b23 + a13 * b33;\n\n te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31;\n te[ 4 ] = a21 * b12 + a22 * b22 + a23 * b32;\n te[ 7 ] = a21 * b13 + a22 * b23 + a23 * b33;\n\n te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31;\n te[ 5 ] = a31 * b12 + a32 * b22 + a33 * b32;\n te[ 8 ] = a31 * b13 + a32 * b23 + a33 * b33;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n var te = this.elements;\n\n te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s;\n te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s;\n te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s;\n\n return this;\n\n },\n\n determinant: function () {\n\n var te = this.elements;\n\n var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ],\n d = te[ 3 ], e = te[ 4 ], f = te[ 5 ],\n g = te[ 6 ], h = te[ 7 ], i = te[ 8 ];\n\n return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g;\n\n },\n\n getInverse: function ( matrix, throwOnDegenerate ) {\n\n if ( matrix && matrix.isMatrix4 ) {\n\n console.error( \"THREE.Matrix3: .getInverse() no longer takes a Matrix4 argument.\" );\n\n }\n\n var me = matrix.elements,\n te = this.elements,\n\n n11 = me[ 0 ], n21 = me[ 1 ], n31 = me[ 2 ],\n n12 = me[ 3 ], n22 = me[ 4 ], n32 = me[ 5 ],\n n13 = me[ 6 ], n23 = me[ 7 ], n33 = me[ 8 ],\n\n t11 = n33 * n22 - n32 * n23,\n t12 = n32 * n13 - n33 * n12,\n t13 = n23 * n12 - n22 * n13,\n\n det = n11 * t11 + n21 * t12 + n31 * t13;\n\n if ( det === 0 ) {\n\n var msg = \"THREE.Matrix3: .getInverse() can't invert matrix, determinant is 0\";\n\n if ( throwOnDegenerate === true ) {\n\n throw new Error( msg );\n\n } else {\n\n console.warn( msg );\n\n }\n\n return this.identity();\n\n }\n\n var detInv = 1 / det;\n\n te[ 0 ] = t11 * detInv;\n te[ 1 ] = ( n31 * n23 - n33 * n21 ) * detInv;\n te[ 2 ] = ( n32 * n21 - n31 * n22 ) * detInv;\n\n te[ 3 ] = t12 * detInv;\n te[ 4 ] = ( n33 * n11 - n31 * n13 ) * detInv;\n te[ 5 ] = ( n31 * n12 - n32 * n11 ) * detInv;\n\n te[ 6 ] = t13 * detInv;\n te[ 7 ] = ( n21 * n13 - n23 * n11 ) * detInv;\n te[ 8 ] = ( n22 * n11 - n21 * n12 ) * detInv;\n\n return this;\n\n },\n\n transpose: function () {\n\n var tmp, m = this.elements;\n\n tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp;\n tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp;\n tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp;\n\n return this;\n\n },\n\n getNormalMatrix: function ( matrix4 ) {\n\n return this.setFromMatrix4( matrix4 ).getInverse( this ).transpose();\n\n },\n\n transposeIntoArray: function ( r ) {\n\n var m = this.elements;\n\n r[ 0 ] = m[ 0 ];\n r[ 1 ] = m[ 3 ];\n r[ 2 ] = m[ 6 ];\n r[ 3 ] = m[ 1 ];\n r[ 4 ] = m[ 4 ];\n r[ 5 ] = m[ 7 ];\n r[ 6 ] = m[ 2 ];\n r[ 7 ] = m[ 5 ];\n r[ 8 ] = m[ 8 ];\n\n return this;\n\n },\n\n setUvTransform: function ( tx, ty, sx, sy, rotation, cx, cy ) {\n\n var c = Math.cos( rotation );\n var s = Math.sin( rotation );\n\n this.set(\n sx * c, sx * s, - sx * ( c * cx + s * cy ) + cx + tx,\n - sy * s, sy * c, - sy * ( - s * cx + c * cy ) + cy + ty,\n 0, 0, 1\n );\n\n },\n\n scale: function ( sx, sy ) {\n\n var te = this.elements;\n\n te[ 0 ] *= sx; te[ 3 ] *= sx; te[ 6 ] *= sx;\n te[ 1 ] *= sy; te[ 4 ] *= sy; te[ 7 ] *= sy;\n\n return this;\n\n },\n\n rotate: function ( theta ) {\n\n var c = Math.cos( theta );\n var s = Math.sin( theta );\n\n var te = this.elements;\n\n var a11 = te[ 0 ], a12 = te[ 3 ], a13 = te[ 6 ];\n var a21 = te[ 1 ], a22 = te[ 4 ], a23 = te[ 7 ];\n\n te[ 0 ] = c * a11 + s * a21;\n te[ 3 ] = c * a12 + s * a22;\n te[ 6 ] = c * a13 + s * a23;\n\n te[ 1 ] = - s * a11 + c * a21;\n te[ 4 ] = - s * a12 + c * a22;\n te[ 7 ] = - s * a13 + c * a23;\n\n return this;\n\n },\n\n translate: function ( tx, ty ) {\n\n var te = this.elements;\n\n te[ 0 ] += tx * te[ 2 ]; te[ 3 ] += tx * te[ 5 ]; te[ 6 ] += tx * te[ 8 ];\n te[ 1 ] += ty * te[ 2 ]; te[ 4 ] += ty * te[ 5 ]; te[ 7 ] += ty * te[ 8 ];\n\n return this;\n\n },\n\n equals: function ( matrix ) {\n\n var te = this.elements;\n var me = matrix.elements;\n\n for ( var i = 0; i < 9; i ++ ) {\n\n if ( te[ i ] !== me[ i ] ) return false;\n\n }\n\n return true;\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n for ( var i = 0; i < 9; i ++ ) {\n\n this.elements[ i ] = array[ i + offset ];\n\n }\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n var te = this.elements;\n\n array[ offset ] = te[ 0 ];\n array[ offset + 1 ] = te[ 1 ];\n array[ offset + 2 ] = te[ 2 ];\n\n array[ offset + 3 ] = te[ 3 ];\n array[ offset + 4 ] = te[ 4 ];\n array[ offset + 5 ] = te[ 5 ];\n\n array[ offset + 6 ] = te[ 6 ];\n array[ offset + 7 ] = te[ 7 ];\n array[ offset + 8 ] = te[ 8 ];\n\n return array;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author szimek / https://github.com/szimek/\n */\n\n var textureId = 0;\n\n function Texture( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n Object.defineProperty( this, 'id', { value: textureId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n\n this.image = image !== undefined ? image : Texture.DEFAULT_IMAGE;\n this.mipmaps = [];\n\n this.mapping = mapping !== undefined ? mapping : Texture.DEFAULT_MAPPING;\n\n this.wrapS = wrapS !== undefined ? wrapS : ClampToEdgeWrapping;\n this.wrapT = wrapT !== undefined ? wrapT : ClampToEdgeWrapping;\n\n this.magFilter = magFilter !== undefined ? magFilter : LinearFilter;\n this.minFilter = minFilter !== undefined ? minFilter : LinearMipMapLinearFilter;\n\n this.anisotropy = anisotropy !== undefined ? anisotropy : 1;\n\n this.format = format !== undefined ? format : RGBAFormat;\n this.type = type !== undefined ? type : UnsignedByteType;\n\n this.offset = new Vector2( 0, 0 );\n this.repeat = new Vector2( 1, 1 );\n this.center = new Vector2( 0, 0 );\n this.rotation = 0;\n\n this.matrixAutoUpdate = true;\n this.matrix = new Matrix3();\n\n this.generateMipmaps = true;\n this.premultiplyAlpha = false;\n this.flipY = true;\n this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml)\n\n // Values of encoding !== THREE.LinearEncoding only supported on map, envMap and emissiveMap.\n //\n // Also changing the encoding after already used by a Material will not automatically make the Material\n // update. You need to explicitly call Material.needsUpdate to trigger it to recompile.\n this.encoding = encoding !== undefined ? encoding : LinearEncoding;\n\n this.version = 0;\n this.onUpdate = null;\n\n }\n\n Texture.DEFAULT_IMAGE = undefined;\n Texture.DEFAULT_MAPPING = UVMapping;\n\n Texture.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Texture,\n\n isTexture: true,\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.name = source.name;\n\n this.image = source.image;\n this.mipmaps = source.mipmaps.slice( 0 );\n\n this.mapping = source.mapping;\n\n this.wrapS = source.wrapS;\n this.wrapT = source.wrapT;\n\n this.magFilter = source.magFilter;\n this.minFilter = source.minFilter;\n\n this.anisotropy = source.anisotropy;\n\n this.format = source.format;\n this.type = source.type;\n\n this.offset.copy( source.offset );\n this.repeat.copy( source.repeat );\n this.center.copy( source.center );\n this.rotation = source.rotation;\n\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n this.matrix.copy( source.matrix );\n\n this.generateMipmaps = source.generateMipmaps;\n this.premultiplyAlpha = source.premultiplyAlpha;\n this.flipY = source.flipY;\n this.unpackAlignment = source.unpackAlignment;\n this.encoding = source.encoding;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n if ( ! isRootObject && meta.textures[ this.uuid ] !== undefined ) {\n\n return meta.textures[ this.uuid ];\n\n }\n\n function getDataURL( image ) {\n\n var canvas;\n\n if ( image instanceof HTMLCanvasElement ) {\n\n canvas = image;\n\n } else {\n\n canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = image.width;\n canvas.height = image.height;\n\n var context = canvas.getContext( '2d' );\n\n if ( image instanceof ImageData ) {\n\n context.putImageData( image, 0, 0 );\n\n } else {\n\n context.drawImage( image, 0, 0, image.width, image.height );\n\n }\n\n }\n\n if ( canvas.width > 2048 || canvas.height > 2048 ) {\n\n return canvas.toDataURL( 'image/jpeg', 0.6 );\n\n } else {\n\n return canvas.toDataURL( 'image/png' );\n\n }\n\n }\n\n var output = {\n\n metadata: {\n version: 4.5,\n type: 'Texture',\n generator: 'Texture.toJSON'\n },\n\n uuid: this.uuid,\n name: this.name,\n\n mapping: this.mapping,\n\n repeat: [ this.repeat.x, this.repeat.y ],\n offset: [ this.offset.x, this.offset.y ],\n center: [ this.center.x, this.center.y ],\n rotation: this.rotation,\n\n wrap: [ this.wrapS, this.wrapT ],\n\n format: this.format,\n minFilter: this.minFilter,\n magFilter: this.magFilter,\n anisotropy: this.anisotropy,\n\n flipY: this.flipY\n\n };\n\n if ( this.image !== undefined ) {\n\n // TODO: Move to THREE.Image\n\n var image = this.image;\n\n if ( image.uuid === undefined ) {\n\n image.uuid = _Math.generateUUID(); // UGH\n\n }\n\n if ( ! isRootObject && meta.images[ image.uuid ] === undefined ) {\n\n meta.images[ image.uuid ] = {\n uuid: image.uuid,\n url: getDataURL( image )\n };\n\n }\n\n output.image = image.uuid;\n\n }\n\n if ( ! isRootObject ) {\n\n meta.textures[ this.uuid ] = output;\n\n }\n\n return output;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n },\n\n transformUv: function ( uv ) {\n\n if ( this.mapping !== UVMapping ) return;\n\n uv.applyMatrix3( this.matrix );\n\n if ( uv.x < 0 || uv.x > 1 ) {\n\n switch ( this.wrapS ) {\n\n case RepeatWrapping:\n\n uv.x = uv.x - Math.floor( uv.x );\n break;\n\n case ClampToEdgeWrapping:\n\n uv.x = uv.x < 0 ? 0 : 1;\n break;\n\n case MirroredRepeatWrapping:\n\n if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) {\n\n uv.x = Math.ceil( uv.x ) - uv.x;\n\n } else {\n\n uv.x = uv.x - Math.floor( uv.x );\n\n }\n break;\n\n }\n\n }\n\n if ( uv.y < 0 || uv.y > 1 ) {\n\n switch ( this.wrapT ) {\n\n case RepeatWrapping:\n\n uv.y = uv.y - Math.floor( uv.y );\n break;\n\n case ClampToEdgeWrapping:\n\n uv.y = uv.y < 0 ? 0 : 1;\n break;\n\n case MirroredRepeatWrapping:\n\n if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) {\n\n uv.y = Math.ceil( uv.y ) - uv.y;\n\n } else {\n\n uv.y = uv.y - Math.floor( uv.y );\n\n }\n break;\n\n }\n\n }\n\n if ( this.flipY ) {\n\n uv.y = 1 - uv.y;\n\n }\n\n }\n\n } );\n\n Object.defineProperty( Texture.prototype, \"needsUpdate\", {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n /**\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author philogb / http://blog.thejit.org/\n * @author mikael emtinger / http://gomo.se/\n * @author egraether / http://egraether.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Vector4( x, y, z, w ) {\n\n this.x = x || 0;\n this.y = y || 0;\n this.z = z || 0;\n this.w = ( w !== undefined ) ? w : 1;\n\n }\n\n Object.assign( Vector4.prototype, {\n\n isVector4: true,\n\n set: function ( x, y, z, w ) {\n\n this.x = x;\n this.y = y;\n this.z = z;\n this.w = w;\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.x = scalar;\n this.y = scalar;\n this.z = scalar;\n this.w = scalar;\n\n return this;\n\n },\n\n setX: function ( x ) {\n\n this.x = x;\n\n return this;\n\n },\n\n setY: function ( y ) {\n\n this.y = y;\n\n return this;\n\n },\n\n setZ: function ( z ) {\n\n this.z = z;\n\n return this;\n\n },\n\n setW: function ( w ) {\n\n this.w = w;\n\n return this;\n\n },\n\n setComponent: function ( index, value ) {\n\n switch ( index ) {\n\n case 0: this.x = value; break;\n case 1: this.y = value; break;\n case 2: this.z = value; break;\n case 3: this.w = value; break;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n return this;\n\n },\n\n getComponent: function ( index ) {\n\n switch ( index ) {\n\n case 0: return this.x;\n case 1: return this.y;\n case 2: return this.z;\n case 3: return this.w;\n default: throw new Error( 'index is out of range: ' + index );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.x, this.y, this.z, this.w );\n\n },\n\n copy: function ( v ) {\n\n this.x = v.x;\n this.y = v.y;\n this.z = v.z;\n this.w = ( v.w !== undefined ) ? v.w : 1;\n\n return this;\n\n },\n\n add: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );\n return this.addVectors( v, w );\n\n }\n\n this.x += v.x;\n this.y += v.y;\n this.z += v.z;\n this.w += v.w;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.x += s;\n this.y += s;\n this.z += s;\n this.w += s;\n\n return this;\n\n },\n\n addVectors: function ( a, b ) {\n\n this.x = a.x + b.x;\n this.y = a.y + b.y;\n this.z = a.z + b.z;\n this.w = a.w + b.w;\n\n return this;\n\n },\n\n addScaledVector: function ( v, s ) {\n\n this.x += v.x * s;\n this.y += v.y * s;\n this.z += v.z * s;\n this.w += v.w * s;\n\n return this;\n\n },\n\n sub: function ( v, w ) {\n\n if ( w !== undefined ) {\n\n console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );\n return this.subVectors( v, w );\n\n }\n\n this.x -= v.x;\n this.y -= v.y;\n this.z -= v.z;\n this.w -= v.w;\n\n return this;\n\n },\n\n subScalar: function ( s ) {\n\n this.x -= s;\n this.y -= s;\n this.z -= s;\n this.w -= s;\n\n return this;\n\n },\n\n subVectors: function ( a, b ) {\n\n this.x = a.x - b.x;\n this.y = a.y - b.y;\n this.z = a.z - b.z;\n this.w = a.w - b.w;\n\n return this;\n\n },\n\n multiplyScalar: function ( scalar ) {\n\n this.x *= scalar;\n this.y *= scalar;\n this.z *= scalar;\n this.w *= scalar;\n\n return this;\n\n },\n\n applyMatrix4: function ( m ) {\n\n var x = this.x, y = this.y, z = this.z, w = this.w;\n var e = m.elements;\n\n this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w;\n this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w;\n this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w;\n this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w;\n\n return this;\n\n },\n\n divideScalar: function ( scalar ) {\n\n return this.multiplyScalar( 1 / scalar );\n\n },\n\n setAxisAngleFromQuaternion: function ( q ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm\n\n // q is assumed to be normalized\n\n this.w = 2 * Math.acos( q.w );\n\n var s = Math.sqrt( 1 - q.w * q.w );\n\n if ( s < 0.0001 ) {\n\n this.x = 1;\n this.y = 0;\n this.z = 0;\n\n } else {\n\n this.x = q.x / s;\n this.y = q.y / s;\n this.z = q.z / s;\n\n }\n\n return this;\n\n },\n\n setAxisAngleFromRotationMatrix: function ( m ) {\n\n // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var angle, x, y, z, // variables for result\n epsilon = 0.01, // margin to allow for rounding errors\n epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees\n\n te = m.elements,\n\n m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],\n m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],\n m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n if ( ( Math.abs( m12 - m21 ) < epsilon ) &&\n ( Math.abs( m13 - m31 ) < epsilon ) &&\n ( Math.abs( m23 - m32 ) < epsilon ) ) {\n\n // singularity found\n // first check for identity matrix which must have +1 for all terms\n // in leading diagonal and zero in other terms\n\n if ( ( Math.abs( m12 + m21 ) < epsilon2 ) &&\n ( Math.abs( m13 + m31 ) < epsilon2 ) &&\n ( Math.abs( m23 + m32 ) < epsilon2 ) &&\n ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) {\n\n // this singularity is identity matrix so angle = 0\n\n this.set( 1, 0, 0, 0 );\n\n return this; // zero angle, arbitrary axis\n\n }\n\n // otherwise this singularity is angle = 180\n\n angle = Math.PI;\n\n var xx = ( m11 + 1 ) / 2;\n var yy = ( m22 + 1 ) / 2;\n var zz = ( m33 + 1 ) / 2;\n var xy = ( m12 + m21 ) / 4;\n var xz = ( m13 + m31 ) / 4;\n var yz = ( m23 + m32 ) / 4;\n\n if ( ( xx > yy ) && ( xx > zz ) ) {\n\n // m11 is the largest diagonal term\n\n if ( xx < epsilon ) {\n\n x = 0;\n y = 0.707106781;\n z = 0.707106781;\n\n } else {\n\n x = Math.sqrt( xx );\n y = xy / x;\n z = xz / x;\n\n }\n\n } else if ( yy > zz ) {\n\n // m22 is the largest diagonal term\n\n if ( yy < epsilon ) {\n\n x = 0.707106781;\n y = 0;\n z = 0.707106781;\n\n } else {\n\n y = Math.sqrt( yy );\n x = xy / y;\n z = yz / y;\n\n }\n\n } else {\n\n // m33 is the largest diagonal term so base result on this\n\n if ( zz < epsilon ) {\n\n x = 0.707106781;\n y = 0.707106781;\n z = 0;\n\n } else {\n\n z = Math.sqrt( zz );\n x = xz / z;\n y = yz / z;\n\n }\n\n }\n\n this.set( x, y, z, angle );\n\n return this; // return 180 deg rotation\n\n }\n\n // as we have reached here there are no singularities so we can handle normally\n\n var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) +\n ( m13 - m31 ) * ( m13 - m31 ) +\n ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize\n\n if ( Math.abs( s ) < 0.001 ) s = 1;\n\n // prevent divide by zero, should not happen if matrix is orthogonal and should be\n // caught by singularity test above, but I've left it in just in case\n\n this.x = ( m32 - m23 ) / s;\n this.y = ( m13 - m31 ) / s;\n this.z = ( m21 - m12 ) / s;\n this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 );\n\n return this;\n\n },\n\n min: function ( v ) {\n\n this.x = Math.min( this.x, v.x );\n this.y = Math.min( this.y, v.y );\n this.z = Math.min( this.z, v.z );\n this.w = Math.min( this.w, v.w );\n\n return this;\n\n },\n\n max: function ( v ) {\n\n this.x = Math.max( this.x, v.x );\n this.y = Math.max( this.y, v.y );\n this.z = Math.max( this.z, v.z );\n this.w = Math.max( this.w, v.w );\n\n return this;\n\n },\n\n clamp: function ( min, max ) {\n\n // assumes min < max, componentwise\n\n this.x = Math.max( min.x, Math.min( max.x, this.x ) );\n this.y = Math.max( min.y, Math.min( max.y, this.y ) );\n this.z = Math.max( min.z, Math.min( max.z, this.z ) );\n this.w = Math.max( min.w, Math.min( max.w, this.w ) );\n\n return this;\n\n },\n\n clampScalar: function () {\n\n var min, max;\n\n return function clampScalar( minVal, maxVal ) {\n\n if ( min === undefined ) {\n\n min = new Vector4();\n max = new Vector4();\n\n }\n\n min.set( minVal, minVal, minVal, minVal );\n max.set( maxVal, maxVal, maxVal, maxVal );\n\n return this.clamp( min, max );\n\n };\n\n }(),\n\n clampLength: function ( min, max ) {\n\n var length = this.length();\n\n return this.divideScalar( length || 1 ).multiplyScalar( Math.max( min, Math.min( max, length ) ) );\n\n },\n\n floor: function () {\n\n this.x = Math.floor( this.x );\n this.y = Math.floor( this.y );\n this.z = Math.floor( this.z );\n this.w = Math.floor( this.w );\n\n return this;\n\n },\n\n ceil: function () {\n\n this.x = Math.ceil( this.x );\n this.y = Math.ceil( this.y );\n this.z = Math.ceil( this.z );\n this.w = Math.ceil( this.w );\n\n return this;\n\n },\n\n round: function () {\n\n this.x = Math.round( this.x );\n this.y = Math.round( this.y );\n this.z = Math.round( this.z );\n this.w = Math.round( this.w );\n\n return this;\n\n },\n\n roundToZero: function () {\n\n this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );\n this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );\n this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );\n this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w );\n\n return this;\n\n },\n\n negate: function () {\n\n this.x = - this.x;\n this.y = - this.y;\n this.z = - this.z;\n this.w = - this.w;\n\n return this;\n\n },\n\n dot: function ( v ) {\n\n return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w;\n\n },\n\n lengthSq: function () {\n\n return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w;\n\n },\n\n length: function () {\n\n return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w );\n\n },\n\n manhattanLength: function () {\n\n return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w );\n\n },\n\n normalize: function () {\n\n return this.divideScalar( this.length() || 1 );\n\n },\n\n setLength: function ( length ) {\n\n return this.normalize().multiplyScalar( length );\n\n },\n\n lerp: function ( v, alpha ) {\n\n this.x += ( v.x - this.x ) * alpha;\n this.y += ( v.y - this.y ) * alpha;\n this.z += ( v.z - this.z ) * alpha;\n this.w += ( v.w - this.w ) * alpha;\n\n return this;\n\n },\n\n lerpVectors: function ( v1, v2, alpha ) {\n\n return this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 );\n\n },\n\n equals: function ( v ) {\n\n return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.x = array[ offset ];\n this.y = array[ offset + 1 ];\n this.z = array[ offset + 2 ];\n this.w = array[ offset + 3 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.x;\n array[ offset + 1 ] = this.y;\n array[ offset + 2 ] = this.z;\n array[ offset + 3 ] = this.w;\n\n return array;\n\n },\n\n fromBufferAttribute: function ( attribute, index, offset ) {\n\n if ( offset !== undefined ) {\n\n console.warn( 'THREE.Vector4: offset has been removed from .fromBufferAttribute().' );\n\n }\n\n this.x = attribute.getX( index );\n this.y = attribute.getY( index );\n this.z = attribute.getZ( index );\n this.w = attribute.getW( index );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author szimek / https://github.com/szimek/\n * @author alteredq / http://alteredqualia.com/\n * @author Marius Kintel / https://github.com/kintel\n */\n\n /*\n In options, we can specify:\n * Texture parameters for an auto-generated target texture\n * depthBuffer/stencilBuffer: Booleans to indicate if we should generate these buffers\n */\n function WebGLRenderTarget( width, height, options ) {\n\n this.width = width;\n this.height = height;\n\n this.scissor = new Vector4( 0, 0, width, height );\n this.scissorTest = false;\n\n this.viewport = new Vector4( 0, 0, width, height );\n\n options = options || {};\n\n if ( options.minFilter === undefined ) options.minFilter = LinearFilter;\n\n this.texture = new Texture( undefined, undefined, options.wrapS, options.wrapT, options.magFilter, options.minFilter, options.format, options.type, options.anisotropy, options.encoding );\n\n this.depthBuffer = options.depthBuffer !== undefined ? options.depthBuffer : true;\n this.stencilBuffer = options.stencilBuffer !== undefined ? options.stencilBuffer : true;\n this.depthTexture = options.depthTexture !== undefined ? options.depthTexture : null;\n\n }\n\n WebGLRenderTarget.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: WebGLRenderTarget,\n\n isWebGLRenderTarget: true,\n\n setSize: function ( width, height ) {\n\n if ( this.width !== width || this.height !== height ) {\n\n this.width = width;\n this.height = height;\n\n this.dispose();\n\n }\n\n this.viewport.set( 0, 0, width, height );\n this.scissor.set( 0, 0, width, height );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.width = source.width;\n this.height = source.height;\n\n this.viewport.copy( source.viewport );\n\n this.texture = source.texture.clone();\n\n this.depthBuffer = source.depthBuffer;\n this.stencilBuffer = source.stencilBuffer;\n this.depthTexture = source.depthTexture;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com\n */\n\n function WebGLRenderTargetCube( width, height, options ) {\n\n WebGLRenderTarget.call( this, width, height, options );\n\n this.activeCubeFace = 0; // PX 0, NX 1, PY 2, NY 3, PZ 4, NZ 5\n this.activeMipMapLevel = 0;\n\n }\n\n WebGLRenderTargetCube.prototype = Object.create( WebGLRenderTarget.prototype );\n WebGLRenderTargetCube.prototype.constructor = WebGLRenderTargetCube;\n\n WebGLRenderTargetCube.prototype.isWebGLRenderTargetCube = true;\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function DataTexture( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.image = { data: data, width: width, height: height };\n\n this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n this.generateMipmaps = false;\n this.flipY = false;\n this.unpackAlignment = 1;\n\n }\n\n DataTexture.prototype = Object.create( Texture.prototype );\n DataTexture.prototype.constructor = DataTexture;\n\n DataTexture.prototype.isDataTexture = true;\n\n /**\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Box3( min, max ) {\n\n this.min = ( min !== undefined ) ? min : new Vector3( + Infinity, + Infinity, + Infinity );\n this.max = ( max !== undefined ) ? max : new Vector3( - Infinity, - Infinity, - Infinity );\n\n }\n\n Object.assign( Box3.prototype, {\n\n isBox3: true,\n\n set: function ( min, max ) {\n\n this.min.copy( min );\n this.max.copy( max );\n\n return this;\n\n },\n\n setFromArray: function ( array ) {\n\n var minX = + Infinity;\n var minY = + Infinity;\n var minZ = + Infinity;\n\n var maxX = - Infinity;\n var maxY = - Infinity;\n var maxZ = - Infinity;\n\n for ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n var x = array[ i ];\n var y = array[ i + 1 ];\n var z = array[ i + 2 ];\n\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( z < minZ ) minZ = z;\n\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n if ( z > maxZ ) maxZ = z;\n\n }\n\n this.min.set( minX, minY, minZ );\n this.max.set( maxX, maxY, maxZ );\n\n return this;\n\n },\n\n setFromBufferAttribute: function ( attribute ) {\n\n var minX = + Infinity;\n var minY = + Infinity;\n var minZ = + Infinity;\n\n var maxX = - Infinity;\n var maxY = - Infinity;\n var maxZ = - Infinity;\n\n for ( var i = 0, l = attribute.count; i < l; i ++ ) {\n\n var x = attribute.getX( i );\n var y = attribute.getY( i );\n var z = attribute.getZ( i );\n\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( z < minZ ) minZ = z;\n\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n if ( z > maxZ ) maxZ = z;\n\n }\n\n this.min.set( minX, minY, minZ );\n this.max.set( maxX, maxY, maxZ );\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.makeEmpty();\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n this.expandByPoint( points[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromCenterAndSize: function () {\n\n var v1 = new Vector3();\n\n return function setFromCenterAndSize( center, size ) {\n\n var halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n\n this.min.copy( center ).sub( halfSize );\n this.max.copy( center ).add( halfSize );\n\n return this;\n\n };\n\n }(),\n\n setFromObject: function ( object ) {\n\n this.makeEmpty();\n\n return this.expandByObject( object );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( box ) {\n\n this.min.copy( box.min );\n this.max.copy( box.max );\n\n return this;\n\n },\n\n makeEmpty: function () {\n\n this.min.x = this.min.y = this.min.z = + Infinity;\n this.max.x = this.max.y = this.max.z = - Infinity;\n\n return this;\n\n },\n\n isEmpty: function () {\n\n // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z );\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getCenter() target is now required' );\n target = new Vector3();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n },\n\n getSize: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getSize() target is now required' );\n target = new Vector3();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0, 0 ) : target.subVectors( this.max, this.min );\n\n },\n\n expandByPoint: function ( point ) {\n\n this.min.min( point );\n this.max.max( point );\n\n return this;\n\n },\n\n expandByVector: function ( vector ) {\n\n this.min.sub( vector );\n this.max.add( vector );\n\n return this;\n\n },\n\n expandByScalar: function ( scalar ) {\n\n this.min.addScalar( - scalar );\n this.max.addScalar( scalar );\n\n return this;\n\n },\n\n expandByObject: function () {\n\n // Computes the world-axis-aligned bounding box of an object (including its children),\n // accounting for both the object's, and children's, world transforms\n\n var scope, i, l;\n\n var v1 = new Vector3();\n\n function traverse( node ) {\n\n var geometry = node.geometry;\n\n if ( geometry !== undefined ) {\n\n if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n\n for ( i = 0, l = vertices.length; i < l; i ++ ) {\n\n v1.copy( vertices[ i ] );\n v1.applyMatrix4( node.matrixWorld );\n\n scope.expandByPoint( v1 );\n\n }\n\n } else if ( geometry.isBufferGeometry ) {\n\n var attribute = geometry.attributes.position;\n\n if ( attribute !== undefined ) {\n\n for ( i = 0, l = attribute.count; i < l; i ++ ) {\n\n v1.fromBufferAttribute( attribute, i ).applyMatrix4( node.matrixWorld );\n\n scope.expandByPoint( v1 );\n\n }\n\n }\n\n }\n\n }\n\n }\n\n return function expandByObject( object ) {\n\n scope = this;\n\n object.updateMatrixWorld( true );\n\n object.traverse( traverse );\n\n return this;\n\n };\n\n }(),\n\n containsPoint: function ( point ) {\n\n return point.x < this.min.x || point.x > this.max.x ||\n point.y < this.min.y || point.y > this.max.y ||\n point.z < this.min.z || point.z > this.max.z ? false : true;\n\n },\n\n containsBox: function ( box ) {\n\n return this.min.x <= box.min.x && box.max.x <= this.max.x &&\n this.min.y <= box.min.y && box.max.y <= this.max.y &&\n this.min.z <= box.min.z && box.max.z <= this.max.z;\n\n },\n\n getParameter: function ( point, target ) {\n\n // This can potentially have a divide by zero if the box\n // has a size dimension of 0.\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getParameter() target is now required' );\n target = new Vector3();\n\n }\n\n return target.set(\n ( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n ( point.y - this.min.y ) / ( this.max.y - this.min.y ),\n ( point.z - this.min.z ) / ( this.max.z - this.min.z )\n );\n\n },\n\n intersectsBox: function ( box ) {\n\n // using 6 splitting planes to rule out intersections.\n return box.max.x < this.min.x || box.min.x > this.max.x ||\n box.max.y < this.min.y || box.min.y > this.max.y ||\n box.max.z < this.min.z || box.min.z > this.max.z ? false : true;\n\n },\n\n intersectsSphere: ( function () {\n\n var closestPoint = new Vector3();\n\n return function intersectsSphere( sphere ) {\n\n // Find the point on the AABB closest to the sphere center.\n this.clampPoint( sphere.center, closestPoint );\n\n // If that point is inside the sphere, the AABB and sphere intersect.\n return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );\n\n };\n\n } )(),\n\n intersectsPlane: function ( plane ) {\n\n // We compute the minimum and maximum dot product values. If those values\n // are on the same side (back or front) of the plane, then there is no intersection.\n\n var min, max;\n\n if ( plane.normal.x > 0 ) {\n\n min = plane.normal.x * this.min.x;\n max = plane.normal.x * this.max.x;\n\n } else {\n\n min = plane.normal.x * this.max.x;\n max = plane.normal.x * this.min.x;\n\n }\n\n if ( plane.normal.y > 0 ) {\n\n min += plane.normal.y * this.min.y;\n max += plane.normal.y * this.max.y;\n\n } else {\n\n min += plane.normal.y * this.max.y;\n max += plane.normal.y * this.min.y;\n\n }\n\n if ( plane.normal.z > 0 ) {\n\n min += plane.normal.z * this.min.z;\n max += plane.normal.z * this.max.z;\n\n } else {\n\n min += plane.normal.z * this.max.z;\n max += plane.normal.z * this.min.z;\n\n }\n\n return ( min <= plane.constant && max >= plane.constant );\n\n },\n\n intersectsTriangle: ( function () {\n\n // triangle centered vertices\n var v0 = new Vector3();\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n // triangle edge vectors\n var f0 = new Vector3();\n var f1 = new Vector3();\n var f2 = new Vector3();\n\n var testAxis = new Vector3();\n\n var center = new Vector3();\n var extents = new Vector3();\n\n var triangleNormal = new Vector3();\n\n function satForAxes( axes ) {\n\n var i, j;\n\n for ( i = 0, j = axes.length - 3; i <= j; i += 3 ) {\n\n testAxis.fromArray( axes, i );\n // project the aabb onto the seperating axis\n var r = extents.x * Math.abs( testAxis.x ) + extents.y * Math.abs( testAxis.y ) + extents.z * Math.abs( testAxis.z );\n // project all 3 vertices of the triangle onto the seperating axis\n var p0 = v0.dot( testAxis );\n var p1 = v1.dot( testAxis );\n var p2 = v2.dot( testAxis );\n // actual test, basically see if either of the most extreme of the triangle points intersects r\n if ( Math.max( - Math.max( p0, p1, p2 ), Math.min( p0, p1, p2 ) ) > r ) {\n\n // points of the projected triangle are outside the projected half-length of the aabb\n // the axis is seperating and we can exit\n return false;\n\n }\n\n }\n\n return true;\n\n }\n\n return function intersectsTriangle( triangle ) {\n\n if ( this.isEmpty() ) {\n\n return false;\n\n }\n\n // compute box center and extents\n this.getCenter( center );\n extents.subVectors( this.max, center );\n\n // translate triangle to aabb origin\n v0.subVectors( triangle.a, center );\n v1.subVectors( triangle.b, center );\n v2.subVectors( triangle.c, center );\n\n // compute edge vectors for triangle\n f0.subVectors( v1, v0 );\n f1.subVectors( v2, v1 );\n f2.subVectors( v0, v2 );\n\n // test against axes that are given by cross product combinations of the edges of the triangle and the edges of the aabb\n // make an axis testing of each of the 3 sides of the aabb against each of the 3 sides of the triangle = 9 axis of separation\n // axis_ij = u_i x f_j (u0, u1, u2 = face normals of aabb = x,y,z axes vectors since aabb is axis aligned)\n var axes = [\n 0, - f0.z, f0.y, 0, - f1.z, f1.y, 0, - f2.z, f2.y,\n f0.z, 0, - f0.x, f1.z, 0, - f1.x, f2.z, 0, - f2.x,\n - f0.y, f0.x, 0, - f1.y, f1.x, 0, - f2.y, f2.x, 0\n ];\n if ( ! satForAxes( axes ) ) {\n\n return false;\n\n }\n\n // test 3 face normals from the aabb\n axes = [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ];\n if ( ! satForAxes( axes ) ) {\n\n return false;\n\n }\n\n // finally testing the face normal of the triangle\n // use already existing triangle edge vectors here\n triangleNormal.crossVectors( f0, f1 );\n axes = [ triangleNormal.x, triangleNormal.y, triangleNormal.z ];\n return satForAxes( axes );\n\n };\n\n } )(),\n\n clampPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .clampPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( point ).clamp( this.min, this.max );\n\n },\n\n distanceToPoint: function () {\n\n var v1 = new Vector3();\n\n return function distanceToPoint( point ) {\n\n var clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n return clampedPoint.sub( point ).length();\n\n };\n\n }(),\n\n getBoundingSphere: function () {\n\n var v1 = new Vector3();\n\n return function getBoundingSphere( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box3: .getBoundingSphere() target is now required' );\n target = new Sphere();\n\n }\n\n this.getCenter( target.center );\n\n target.radius = this.getSize( v1 ).length() * 0.5;\n\n return target;\n\n };\n\n }(),\n\n intersect: function ( box ) {\n\n this.min.max( box.min );\n this.max.min( box.max );\n\n // ensure that if there is no overlap, the result is fully empty, not slightly empty with non-inf/+inf values that will cause subsequence intersects to erroneously return valid values.\n if ( this.isEmpty() ) this.makeEmpty();\n\n return this;\n\n },\n\n union: function ( box ) {\n\n this.min.min( box.min );\n this.max.max( box.max );\n\n return this;\n\n },\n\n applyMatrix4: function () {\n\n var points = [\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3(),\n new Vector3()\n ];\n\n return function applyMatrix4( matrix ) {\n\n // transform of empty box is an empty box.\n if ( this.isEmpty() ) return this;\n\n // NOTE: I am using a binary pattern to specify all 2^3 combinations below\n points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000\n points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001\n points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010\n points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011\n points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100\n points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101\n points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110\n points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111\n\n this.setFromPoints( points );\n\n return this;\n\n };\n\n }(),\n\n translate: function ( offset ) {\n\n this.min.add( offset );\n this.max.add( offset );\n\n return this;\n\n },\n\n equals: function ( box ) {\n\n return box.min.equals( this.min ) && box.max.equals( this.max );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Sphere( center, radius ) {\n\n this.center = ( center !== undefined ) ? center : new Vector3();\n this.radius = ( radius !== undefined ) ? radius : 0;\n\n }\n\n Object.assign( Sphere.prototype, {\n\n set: function ( center, radius ) {\n\n this.center.copy( center );\n this.radius = radius;\n\n return this;\n\n },\n\n setFromPoints: function () {\n\n var box = new Box3();\n\n return function setFromPoints( points, optionalCenter ) {\n\n var center = this.center;\n\n if ( optionalCenter !== undefined ) {\n\n center.copy( optionalCenter );\n\n } else {\n\n box.setFromPoints( points ).getCenter( center );\n\n }\n\n var maxRadiusSq = 0;\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) );\n\n }\n\n this.radius = Math.sqrt( maxRadiusSq );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( sphere ) {\n\n this.center.copy( sphere.center );\n this.radius = sphere.radius;\n\n return this;\n\n },\n\n empty: function () {\n\n return ( this.radius <= 0 );\n\n },\n\n containsPoint: function ( point ) {\n\n return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) );\n\n },\n\n distanceToPoint: function ( point ) {\n\n return ( point.distanceTo( this.center ) - this.radius );\n\n },\n\n intersectsSphere: function ( sphere ) {\n\n var radiusSum = this.radius + sphere.radius;\n\n return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsSphere( this );\n\n },\n\n intersectsPlane: function ( plane ) {\n\n return Math.abs( plane.distanceToPoint( this.center ) ) <= this.radius;\n\n },\n\n clampPoint: function ( point, target ) {\n\n var deltaLengthSq = this.center.distanceToSquared( point );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Sphere: .clampPoint() target is now required' );\n target = new Vector3();\n\n }\n\n target.copy( point );\n\n if ( deltaLengthSq > ( this.radius * this.radius ) ) {\n\n target.sub( this.center ).normalize();\n target.multiplyScalar( this.radius ).add( this.center );\n\n }\n\n return target;\n\n },\n\n getBoundingBox: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Sphere: .getBoundingBox() target is now required' );\n target = new Box3();\n\n }\n\n target.set( this.center, this.center );\n target.expandByScalar( this.radius );\n\n return target;\n\n },\n\n applyMatrix4: function ( matrix ) {\n\n this.center.applyMatrix4( matrix );\n this.radius = this.radius * matrix.getMaxScaleOnAxis();\n\n return this;\n\n },\n\n translate: function ( offset ) {\n\n this.center.add( offset );\n\n return this;\n\n },\n\n equals: function ( sphere ) {\n\n return sphere.center.equals( this.center ) && ( sphere.radius === this.radius );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Plane( normal, constant ) {\n\n // normal is assumed to be normalized\n\n this.normal = ( normal !== undefined ) ? normal : new Vector3( 1, 0, 0 );\n this.constant = ( constant !== undefined ) ? constant : 0;\n\n }\n\n Object.assign( Plane.prototype, {\n\n set: function ( normal, constant ) {\n\n this.normal.copy( normal );\n this.constant = constant;\n\n return this;\n\n },\n\n setComponents: function ( x, y, z, w ) {\n\n this.normal.set( x, y, z );\n this.constant = w;\n\n return this;\n\n },\n\n setFromNormalAndCoplanarPoint: function ( normal, point ) {\n\n this.normal.copy( normal );\n this.constant = - point.dot( this.normal );\n\n return this;\n\n },\n\n setFromCoplanarPoints: function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function setFromCoplanarPoints( a, b, c ) {\n\n var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize();\n\n // Q: should an error be thrown if normal is zero (e.g. degenerate plane)?\n\n this.setFromNormalAndCoplanarPoint( normal, a );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( plane ) {\n\n this.normal.copy( plane.normal );\n this.constant = plane.constant;\n\n return this;\n\n },\n\n normalize: function () {\n\n // Note: will lead to a divide by zero if the plane is invalid.\n\n var inverseNormalLength = 1.0 / this.normal.length();\n this.normal.multiplyScalar( inverseNormalLength );\n this.constant *= inverseNormalLength;\n\n return this;\n\n },\n\n negate: function () {\n\n this.constant *= - 1;\n this.normal.negate();\n\n return this;\n\n },\n\n distanceToPoint: function ( point ) {\n\n return this.normal.dot( point ) + this.constant;\n\n },\n\n distanceToSphere: function ( sphere ) {\n\n return this.distanceToPoint( sphere.center ) - sphere.radius;\n\n },\n\n projectPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .projectPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.normal ).multiplyScalar( - this.distanceToPoint( point ) ).add( point );\n\n },\n\n intersectLine: function () {\n\n var v1 = new Vector3();\n\n return function intersectLine( line, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .intersectLine() target is now required' );\n target = new Vector3();\n\n }\n\n var direction = line.delta( v1 );\n\n var denominator = this.normal.dot( direction );\n\n if ( denominator === 0 ) {\n\n // line is coplanar, return origin\n if ( this.distanceToPoint( line.start ) === 0 ) {\n\n return target.copy( line.start );\n\n }\n\n // Unsure if this is the correct method to handle this case.\n return undefined;\n\n }\n\n var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator;\n\n if ( t < 0 || t > 1 ) {\n\n return undefined;\n\n }\n\n return target.copy( direction ).multiplyScalar( t ).add( line.start );\n\n };\n\n }(),\n\n intersectsLine: function ( line ) {\n\n // Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it.\n\n var startSign = this.distanceToPoint( line.start );\n var endSign = this.distanceToPoint( line.end );\n\n return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsPlane( this );\n\n },\n\n intersectsSphere: function ( sphere ) {\n\n return sphere.intersectsPlane( this );\n\n },\n\n coplanarPoint: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Plane: .coplanarPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.normal ).multiplyScalar( - this.constant );\n\n },\n\n applyMatrix4: function () {\n\n var v1 = new Vector3();\n var m1 = new Matrix3();\n\n return function applyMatrix4( matrix, optionalNormalMatrix ) {\n\n var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix );\n\n var referencePoint = this.coplanarPoint( v1 ).applyMatrix4( matrix );\n\n var normal = this.normal.applyMatrix3( normalMatrix ).normalize();\n\n this.constant = - referencePoint.dot( normal );\n\n return this;\n\n };\n\n }(),\n\n translate: function ( offset ) {\n\n this.constant -= offset.dot( this.normal );\n\n return this;\n\n },\n\n equals: function ( plane ) {\n\n return plane.normal.equals( this.normal ) && ( plane.constant === this.constant );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author bhouston / http://clara.io\n */\n\n function Frustum( p0, p1, p2, p3, p4, p5 ) {\n\n this.planes = [\n\n ( p0 !== undefined ) ? p0 : new Plane(),\n ( p1 !== undefined ) ? p1 : new Plane(),\n ( p2 !== undefined ) ? p2 : new Plane(),\n ( p3 !== undefined ) ? p3 : new Plane(),\n ( p4 !== undefined ) ? p4 : new Plane(),\n ( p5 !== undefined ) ? p5 : new Plane()\n\n ];\n\n }\n\n Object.assign( Frustum.prototype, {\n\n set: function ( p0, p1, p2, p3, p4, p5 ) {\n\n var planes = this.planes;\n\n planes[ 0 ].copy( p0 );\n planes[ 1 ].copy( p1 );\n planes[ 2 ].copy( p2 );\n planes[ 3 ].copy( p3 );\n planes[ 4 ].copy( p4 );\n planes[ 5 ].copy( p5 );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( frustum ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n planes[ i ].copy( frustum.planes[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromMatrix: function ( m ) {\n\n var planes = this.planes;\n var me = m.elements;\n var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ];\n var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ];\n var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ];\n var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ];\n\n planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize();\n planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize();\n planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize();\n planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize();\n planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize();\n planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize();\n\n return this;\n\n },\n\n intersectsObject: function () {\n\n var sphere = new Sphere();\n\n return function intersectsObject( object ) {\n\n var geometry = object.geometry;\n\n if ( geometry.boundingSphere === null )\n geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere )\n .applyMatrix4( object.matrixWorld );\n\n return this.intersectsSphere( sphere );\n\n };\n\n }(),\n\n intersectsSprite: function () {\n\n var sphere = new Sphere();\n\n return function intersectsSprite( sprite ) {\n\n sphere.center.set( 0, 0, 0 );\n sphere.radius = 0.7071067811865476;\n sphere.applyMatrix4( sprite.matrixWorld );\n\n return this.intersectsSphere( sphere );\n\n };\n\n }(),\n\n intersectsSphere: function ( sphere ) {\n\n var planes = this.planes;\n var center = sphere.center;\n var negRadius = - sphere.radius;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n var distance = planes[ i ].distanceToPoint( center );\n\n if ( distance < negRadius ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n },\n\n intersectsBox: function () {\n\n var p1 = new Vector3(),\n p2 = new Vector3();\n\n return function intersectsBox( box ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n var plane = planes[ i ];\n\n p1.x = plane.normal.x > 0 ? box.min.x : box.max.x;\n p2.x = plane.normal.x > 0 ? box.max.x : box.min.x;\n p1.y = plane.normal.y > 0 ? box.min.y : box.max.y;\n p2.y = plane.normal.y > 0 ? box.max.y : box.min.y;\n p1.z = plane.normal.z > 0 ? box.min.z : box.max.z;\n p2.z = plane.normal.z > 0 ? box.max.z : box.min.z;\n\n var d1 = plane.distanceToPoint( p1 );\n var d2 = plane.distanceToPoint( p2 );\n\n // if both outside plane, no intersection\n\n if ( d1 < 0 && d2 < 0 ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n };\n\n }(),\n\n containsPoint: function ( point ) {\n\n var planes = this.planes;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( planes[ i ].distanceToPoint( point ) < 0 ) {\n\n return false;\n\n }\n\n }\n\n return true;\n\n }\n\n } );\n\n var alphamap_fragment = \"#ifdef USE_ALPHAMAP\\n\\tdiffuseColor.a *= texture2D( alphaMap, vUv ).g;\\n#endif\\n\";\n\n var alphamap_pars_fragment = \"#ifdef USE_ALPHAMAP\\n\\tuniform sampler2D alphaMap;\\n#endif\\n\";\n\n var alphatest_fragment = \"#ifdef ALPHATEST\\n\\tif ( diffuseColor.a < ALPHATEST ) discard;\\n#endif\\n\";\n\n var aomap_fragment = \"#ifdef USE_AOMAP\\n\\tfloat ambientOcclusion = ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\\n\\treflectedLight.indirectDiffuse *= ambientOcclusion;\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\treflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, ambientOcclusion, material.specularRoughness );\\n\\t#endif\\n#endif\\n\";\n\n var aomap_pars_fragment = \"#ifdef USE_AOMAP\\n\\tuniform sampler2D aoMap;\\n\\tuniform float aoMapIntensity;\\n#endif\";\n\n var begin_vertex = \"\\nvec3 transformed = vec3( position );\\n\";\n\n var beginnormal_vertex = \"\\nvec3 objectNormal = vec3( normal );\\n\";\n\n var bsdfs = \"float punctualLightIntensityToIrradianceFactor( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\\n\\tif( decayExponent > 0.0 ) {\\n#if defined ( PHYSICALLY_CORRECT_LIGHTS )\\n\\t\\tfloat distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );\\n\\t\\tfloat maxDistanceCutoffFactor = pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );\\n\\t\\treturn distanceFalloff * maxDistanceCutoffFactor;\\n#else\\n\\t\\treturn pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\\n#endif\\n\\t}\\n\\treturn 1.0;\\n}\\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\\n\\treturn RECIPROCAL_PI * diffuseColor;\\n}\\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\\n\\tfloat fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\\n\\treturn ( 1.0 - specularColor ) * fresnel + specularColor;\\n}\\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gl = dotNL + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\tfloat gv = dotNV + sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\treturn 1.0 / ( gl * gv );\\n}\\nfloat G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );\\n\\tfloat gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );\\n\\treturn 0.5 / max( gv + gl, EPSILON );\\n}\\nfloat D_GGX( const in float alpha, const in float dotNH ) {\\n\\tfloat a2 = pow2( alpha );\\n\\tfloat denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0;\\n\\treturn RECIPROCAL_PI * a2 / pow2( denom );\\n}\\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat alpha = pow2( roughness );\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );\\n\\tfloat D = D_GGX( alpha, dotNH );\\n\\treturn F * ( G * D );\\n}\\nvec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) {\\n\\tconst float LUT_SIZE = 64.0;\\n\\tconst float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;\\n\\tconst float LUT_BIAS = 0.5 / LUT_SIZE;\\n\\tfloat dotNV = saturate( dot( N, V ) );\\n\\tvec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) );\\n\\tuv = uv * LUT_SCALE + LUT_BIAS;\\n\\treturn uv;\\n}\\nfloat LTC_ClippedSphereFormFactor( const in vec3 f ) {\\n\\tfloat l = length( f );\\n\\treturn max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 );\\n}\\nvec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) {\\n\\tfloat x = dot( v1, v2 );\\n\\tfloat y = abs( x );\\n\\tfloat a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y;\\n\\tfloat b = 3.4175940 + ( 4.1616724 + y ) * y;\\n\\tfloat v = a / b;\\n\\tfloat theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v;\\n\\treturn cross( v1, v2 ) * theta_sintheta;\\n}\\nvec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {\\n\\tvec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];\\n\\tvec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];\\n\\tvec3 lightNormal = cross( v1, v2 );\\n\\tif( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );\\n\\tvec3 T1, T2;\\n\\tT1 = normalize( V - N * dot( V, N ) );\\n\\tT2 = - cross( N, T1 );\\n\\tmat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );\\n\\tvec3 coords[ 4 ];\\n\\tcoords[ 0 ] = mat * ( rectCoords[ 0 ] - P );\\n\\tcoords[ 1 ] = mat * ( rectCoords[ 1 ] - P );\\n\\tcoords[ 2 ] = mat * ( rectCoords[ 2 ] - P );\\n\\tcoords[ 3 ] = mat * ( rectCoords[ 3 ] - P );\\n\\tcoords[ 0 ] = normalize( coords[ 0 ] );\\n\\tcoords[ 1 ] = normalize( coords[ 1 ] );\\n\\tcoords[ 2 ] = normalize( coords[ 2 ] );\\n\\tcoords[ 3 ] = normalize( coords[ 3 ] );\\n\\tvec3 vectorFormFactor = vec3( 0.0 );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );\\n\\tvectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );\\n\\tfloat result = LTC_ClippedSphereFormFactor( vectorFormFactor );\\n\\treturn vec3( result );\\n}\\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\\n\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\tconst vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\\n\\tconst vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\\n\\tvec4 r = roughness * c0 + c1;\\n\\tfloat a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\\n\\tvec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\\n\\treturn specularColor * AB.x + AB.y;\\n}\\nfloat G_BlinnPhong_Implicit( ) {\\n\\treturn 0.25;\\n}\\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\\n\\treturn RECIPROCAL_PI * ( shininess * 0.5 + 1.0 ) * pow( dotNH, shininess );\\n}\\nvec3 BRDF_Specular_BlinnPhong( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float shininess ) {\\n\\tvec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\\n\\tfloat dotNH = saturate( dot( geometry.normal, halfDir ) );\\n\\tfloat dotLH = saturate( dot( incidentLight.direction, halfDir ) );\\n\\tvec3 F = F_Schlick( specularColor, dotLH );\\n\\tfloat G = G_BlinnPhong_Implicit( );\\n\\tfloat D = D_BlinnPhong( shininess, dotNH );\\n\\treturn F * ( G * D );\\n}\\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\\n\\treturn ( 2.0 / pow2( ggxRoughness + 0.0001 ) - 2.0 );\\n}\\nfloat BlinnExponentToGGXRoughness( const in float blinnExponent ) {\\n\\treturn sqrt( 2.0 / ( blinnExponent + 2.0 ) );\\n}\\n\";\n\n var bumpmap_pars_fragment = \"#ifdef USE_BUMPMAP\\n\\tuniform sampler2D bumpMap;\\n\\tuniform float bumpScale;\\n\\tvec2 dHdxy_fwd() {\\n\\t\\tvec2 dSTdx = dFdx( vUv );\\n\\t\\tvec2 dSTdy = dFdy( vUv );\\n\\t\\tfloat Hll = bumpScale * texture2D( bumpMap, vUv ).x;\\n\\t\\tfloat dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\\n\\t\\tfloat dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\\n\\t\\treturn vec2( dBx, dBy );\\n\\t}\\n\\tvec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\\n\\t\\tvec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );\\n\\t\\tvec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );\\n\\t\\tvec3 vN = surf_norm;\\n\\t\\tvec3 R1 = cross( vSigmaY, vN );\\n\\t\\tvec3 R2 = cross( vN, vSigmaX );\\n\\t\\tfloat fDet = dot( vSigmaX, R1 );\\n\\t\\tvec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\\n\\t\\treturn normalize( abs( fDet ) * surf_norm - vGrad );\\n\\t}\\n#endif\\n\";\n\n var clipping_planes_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\tvec4 plane;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) {\\n\\t\\tplane = clippingPlanes[ i ];\\n\\t\\tif ( dot( vViewPosition, plane.xyz ) > plane.w ) discard;\\n\\t}\\n\\t#if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES\\n\\t\\tbool clipped = true;\\n\\t\\t#pragma unroll_loop\\n\\t\\tfor ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) {\\n\\t\\t\\tplane = clippingPlanes[ i ];\\n\\t\\t\\tclipped = ( dot( vViewPosition, plane.xyz ) > plane.w ) && clipped;\\n\\t\\t}\\n\\t\\tif ( clipped ) discard;\\n\\t#endif\\n#endif\\n\";\n\n var clipping_planes_pars_fragment = \"#if NUM_CLIPPING_PLANES > 0\\n\\t#if ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\t\\tvarying vec3 vViewPosition;\\n\\t#endif\\n\\tuniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ];\\n#endif\\n\";\n\n var clipping_planes_pars_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n\";\n\n var clipping_planes_vertex = \"#if NUM_CLIPPING_PLANES > 0 && ! defined( PHYSICAL ) && ! defined( PHONG )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n\";\n\n var color_fragment = \"#ifdef USE_COLOR\\n\\tdiffuseColor.rgb *= vColor;\\n#endif\";\n\n var color_pars_fragment = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\\n\";\n\n var color_pars_vertex = \"#ifdef USE_COLOR\\n\\tvarying vec3 vColor;\\n#endif\";\n\n var color_vertex = \"#ifdef USE_COLOR\\n\\tvColor.xyz = color.xyz;\\n#endif\";\n\n var common = \"#define PI 3.14159265359\\n#define PI2 6.28318530718\\n#define PI_HALF 1.5707963267949\\n#define RECIPROCAL_PI 0.31830988618\\n#define RECIPROCAL_PI2 0.15915494\\n#define LOG2 1.442695\\n#define EPSILON 1e-6\\n#define saturate(a) clamp( a, 0.0, 1.0 )\\n#define whiteCompliment(a) ( 1.0 - saturate( a ) )\\nfloat pow2( const in float x ) { return x*x; }\\nfloat pow3( const in float x ) { return x*x*x; }\\nfloat pow4( const in float x ) { float x2 = x*x; return x2*x2; }\\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\\nhighp float rand( const in vec2 uv ) {\\n\\tconst highp float a = 12.9898, b = 78.233, c = 43758.5453;\\n\\thighp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI );\\n\\treturn fract(sin(sn) * c);\\n}\\nstruct IncidentLight {\\n\\tvec3 color;\\n\\tvec3 direction;\\n\\tbool visible;\\n};\\nstruct ReflectedLight {\\n\\tvec3 directDiffuse;\\n\\tvec3 directSpecular;\\n\\tvec3 indirectDiffuse;\\n\\tvec3 indirectSpecular;\\n};\\nstruct GeometricContext {\\n\\tvec3 position;\\n\\tvec3 normal;\\n\\tvec3 viewDir;\\n};\\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\\n}\\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\\n\\treturn normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\\n}\\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\tfloat distance = dot( planeNormal, point - pointOnPlane );\\n\\treturn - distance * planeNormal + point;\\n}\\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn sign( dot( point - pointOnPlane, planeNormal ) );\\n}\\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\\n\\treturn lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\\n}\\nmat3 transposeMat3( const in mat3 m ) {\\n\\tmat3 tmp;\\n\\ttmp[ 0 ] = vec3( m[ 0 ].x, m[ 1 ].x, m[ 2 ].x );\\n\\ttmp[ 1 ] = vec3( m[ 0 ].y, m[ 1 ].y, m[ 2 ].y );\\n\\ttmp[ 2 ] = vec3( m[ 0 ].z, m[ 1 ].z, m[ 2 ].z );\\n\\treturn tmp;\\n}\\nfloat linearToRelativeLuminance( const in vec3 color ) {\\n\\tvec3 weights = vec3( 0.2126, 0.7152, 0.0722 );\\n\\treturn dot( weights, color.rgb );\\n}\\n\";\n\n var cube_uv_reflection_fragment = \"#ifdef ENVMAP_TYPE_CUBE_UV\\n#define cubeUV_textureSize (1024.0)\\nint getFaceFromDirection(vec3 direction) {\\n\\tvec3 absDirection = abs(direction);\\n\\tint face = -1;\\n\\tif( absDirection.x > absDirection.z ) {\\n\\t\\tif(absDirection.x > absDirection.y )\\n\\t\\t\\tface = direction.x > 0.0 ? 0 : 3;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\telse {\\n\\t\\tif(absDirection.z > absDirection.y )\\n\\t\\t\\tface = direction.z > 0.0 ? 2 : 5;\\n\\t\\telse\\n\\t\\t\\tface = direction.y > 0.0 ? 1 : 4;\\n\\t}\\n\\treturn face;\\n}\\n#define cubeUV_maxLods1 (log2(cubeUV_textureSize*0.25) - 1.0)\\n#define cubeUV_rangeClamp (exp2((6.0 - 1.0) * 2.0))\\nvec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness ) {\\n\\tfloat scale = exp2(cubeUV_maxLods1 - roughnessLevel);\\n\\tfloat dxRoughness = dFdx(roughness);\\n\\tfloat dyRoughness = dFdy(roughness);\\n\\tvec3 dx = dFdx( vec * scale * dxRoughness );\\n\\tvec3 dy = dFdy( vec * scale * dyRoughness );\\n\\tfloat d = max( dot( dx, dx ), dot( dy, dy ) );\\n\\td = clamp(d, 1.0, cubeUV_rangeClamp);\\n\\tfloat mipLevel = 0.5 * log2(d);\\n\\treturn vec2(floor(mipLevel), fract(mipLevel));\\n}\\n#define cubeUV_maxLods2 (log2(cubeUV_textureSize*0.25) - 2.0)\\n#define cubeUV_rcpTextureSize (1.0 / cubeUV_textureSize)\\nvec2 getCubeUV(vec3 direction, float roughnessLevel, float mipLevel) {\\n\\tmipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;\\n\\tfloat a = 16.0 * cubeUV_rcpTextureSize;\\n\\tvec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );\\n\\tvec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;\\n\\tfloat powScale = exp2_packed.x * exp2_packed.y;\\n\\tfloat scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;\\n\\tfloat mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;\\n\\tbool bRes = mipLevel == 0.0;\\n\\tscale = bRes && (scale < a) ? a : scale;\\n\\tvec3 r;\\n\\tvec2 offset;\\n\\tint face = getFaceFromDirection(direction);\\n\\tfloat rcpPowScale = 1.0 / powScale;\\n\\tif( face == 0) {\\n\\t\\tr = vec3(direction.x, -direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 1) {\\n\\t\\tr = vec3(direction.y, direction.x, direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 2) {\\n\\t\\tr = vec3(direction.z, direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;\\n\\t}\\n\\telse if( face == 3) {\\n\\t\\tr = vec3(direction.x, direction.z, direction.y);\\n\\t\\toffset = vec2(0.0+mipOffset,0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse if( face == 4) {\\n\\t\\tr = vec3(direction.y, direction.x, -direction.z);\\n\\t\\toffset = vec2(scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\telse {\\n\\t\\tr = vec3(direction.z, -direction.x, direction.y);\\n\\t\\toffset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);\\n\\t\\toffset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;\\n\\t}\\n\\tr = normalize(r);\\n\\tfloat texelOffset = 0.5 * cubeUV_rcpTextureSize;\\n\\tvec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;\\n\\tvec2 base = offset + vec2( texelOffset );\\n\\treturn base + s * ( scale - 2.0 * texelOffset );\\n}\\n#define cubeUV_maxLods3 (log2(cubeUV_textureSize*0.25) - 3.0)\\nvec4 textureCubeUV(vec3 reflectedDirection, float roughness ) {\\n\\tfloat roughnessVal = roughness* cubeUV_maxLods3;\\n\\tfloat r1 = floor(roughnessVal);\\n\\tfloat r2 = r1 + 1.0;\\n\\tfloat t = fract(roughnessVal);\\n\\tvec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness);\\n\\tfloat s = mipInfo.y;\\n\\tfloat level0 = mipInfo.x;\\n\\tfloat level1 = level0 + 1.0;\\n\\tlevel1 = level1 > 5.0 ? 5.0 : level1;\\n\\tlevel0 += min( floor( s + 0.5 ), 5.0 );\\n\\tvec2 uv_10 = getCubeUV(reflectedDirection, r1, level0);\\n\\tvec4 color10 = envMapTexelToLinear(texture2D(envMap, uv_10));\\n\\tvec2 uv_20 = getCubeUV(reflectedDirection, r2, level0);\\n\\tvec4 color20 = envMapTexelToLinear(texture2D(envMap, uv_20));\\n\\tvec4 result = mix(color10, color20, t);\\n\\treturn vec4(result.rgb, 1.0);\\n}\\n#endif\\n\";\n\n var defaultnormal_vertex = \"vec3 transformedNormal = normalMatrix * objectNormal;\\n#ifdef FLIP_SIDED\\n\\ttransformedNormal = - transformedNormal;\\n#endif\\n\";\n\n var displacementmap_pars_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\tuniform sampler2D displacementMap;\\n\\tuniform float displacementScale;\\n\\tuniform float displacementBias;\\n#endif\\n\";\n\n var displacementmap_vertex = \"#ifdef USE_DISPLACEMENTMAP\\n\\ttransformed += normalize( objectNormal ) * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\\n#endif\\n\";\n\n var emissivemap_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tvec4 emissiveColor = texture2D( emissiveMap, vUv );\\n\\temissiveColor.rgb = emissiveMapTexelToLinear( emissiveColor ).rgb;\\n\\ttotalEmissiveRadiance *= emissiveColor.rgb;\\n#endif\\n\";\n\n var emissivemap_pars_fragment = \"#ifdef USE_EMISSIVEMAP\\n\\tuniform sampler2D emissiveMap;\\n#endif\\n\";\n\n var encodings_fragment = \" gl_FragColor = linearToOutputTexel( gl_FragColor );\\n\";\n\n var encodings_pars_fragment = \"\\nvec4 LinearToLinear( in vec4 value ) {\\n\\treturn value;\\n}\\nvec4 GammaToLinear( in vec4 value, in float gammaFactor ) {\\n\\treturn vec4( pow( value.xyz, vec3( gammaFactor ) ), value.w );\\n}\\nvec4 LinearToGamma( in vec4 value, in float gammaFactor ) {\\n\\treturn vec4( pow( value.xyz, vec3( 1.0 / gammaFactor ) ), value.w );\\n}\\nvec4 sRGBToLinear( in vec4 value ) {\\n\\treturn vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.w );\\n}\\nvec4 LinearTosRGB( in vec4 value ) {\\n\\treturn vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.w );\\n}\\nvec4 RGBEToLinear( in vec4 value ) {\\n\\treturn vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );\\n}\\nvec4 LinearToRGBE( in vec4 value ) {\\n\\tfloat maxComponent = max( max( value.r, value.g ), value.b );\\n\\tfloat fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );\\n\\treturn vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );\\n}\\nvec4 RGBMToLinear( in vec4 value, in float maxRange ) {\\n\\treturn vec4( value.xyz * value.w * maxRange, 1.0 );\\n}\\nvec4 LinearToRGBM( in vec4 value, in float maxRange ) {\\n\\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\\n\\tfloat M = clamp( maxRGB / maxRange, 0.0, 1.0 );\\n\\tM = ceil( M * 255.0 ) / 255.0;\\n\\treturn vec4( value.rgb / ( M * maxRange ), M );\\n}\\nvec4 RGBDToLinear( in vec4 value, in float maxRange ) {\\n\\treturn vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );\\n}\\nvec4 LinearToRGBD( in vec4 value, in float maxRange ) {\\n\\tfloat maxRGB = max( value.x, max( value.g, value.b ) );\\n\\tfloat D = max( maxRange / maxRGB, 1.0 );\\n\\tD = min( floor( D ) / 255.0, 1.0 );\\n\\treturn vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );\\n}\\nconst mat3 cLogLuvM = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );\\nvec4 LinearToLogLuv( in vec4 value ) {\\n\\tvec3 Xp_Y_XYZp = value.rgb * cLogLuvM;\\n\\tXp_Y_XYZp = max(Xp_Y_XYZp, vec3(1e-6, 1e-6, 1e-6));\\n\\tvec4 vResult;\\n\\tvResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;\\n\\tfloat Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;\\n\\tvResult.w = fract(Le);\\n\\tvResult.z = (Le - (floor(vResult.w*255.0))/255.0)/255.0;\\n\\treturn vResult;\\n}\\nconst mat3 cLogLuvInverseM = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );\\nvec4 LogLuvToLinear( in vec4 value ) {\\n\\tfloat Le = value.z * 255.0 + value.w;\\n\\tvec3 Xp_Y_XYZp;\\n\\tXp_Y_XYZp.y = exp2((Le - 127.0) / 2.0);\\n\\tXp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;\\n\\tXp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;\\n\\tvec3 vRGB = Xp_Y_XYZp.rgb * cLogLuvInverseM;\\n\\treturn vec4( max(vRGB, 0.0), 1.0 );\\n}\\n\";\n\n var envmap_fragment = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#else\\n\\t\\tvec3 reflectVec = vReflect;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tvec4 envColor = textureCube( envMap, vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\\n\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\tvec2 sampleUV;\\n\\t\\treflectVec = normalize( reflectVec );\\n\\t\\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\t\\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\tvec4 envColor = texture2D( envMap, sampleUV );\\n\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\treflectVec = normalize( reflectVec );\\n\\t\\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0, 0.0, 1.0 ) );\\n\\t\\tvec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\\n\\t#else\\n\\t\\tvec4 envColor = vec4( 0.0 );\\n\\t#endif\\n\\tenvColor = envMapTexelToLinear( envColor );\\n\\t#ifdef ENVMAP_BLENDING_MULTIPLY\\n\\t\\toutgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_MIX )\\n\\t\\toutgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\\n\\t#elif defined( ENVMAP_BLENDING_ADD )\\n\\t\\toutgoingLight += envColor.xyz * specularStrength * reflectivity;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_pars_fragment = \"#if defined( USE_ENVMAP ) || defined( PHYSICAL )\\n\\tuniform float reflectivity;\\n\\tuniform float envMapIntensity;\\n#endif\\n#ifdef USE_ENVMAP\\n\\t#if ! defined( PHYSICAL ) && ( defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#endif\\n\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\tuniform samplerCube envMap;\\n\\t#else\\n\\t\\tuniform sampler2D envMap;\\n\\t#endif\\n\\tuniform float flipEnvMap;\\n\\tuniform int maxMipLevel;\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( PHYSICAL )\\n\\t\\tuniform float refractionRatio;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_pars_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvarying vec3 vWorldPosition;\\n\\t#else\\n\\t\\tvarying vec3 vReflect;\\n\\t\\tuniform float refractionRatio;\\n\\t#endif\\n#endif\\n\";\n\n var envmap_vertex = \"#ifdef USE_ENVMAP\\n\\t#if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\\n\\t\\tvWorldPosition = worldPosition.xyz;\\n\\t#else\\n\\t\\tvec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\\n\\t\\tvec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvReflect = reflect( cameraToVertex, worldNormal );\\n\\t\\t#else\\n\\t\\t\\tvReflect = refract( cameraToVertex, worldNormal, refractionRatio );\\n\\t\\t#endif\\n\\t#endif\\n#endif\\n\";\n\n var fog_vertex = \"\\n#ifdef USE_FOG\\nfogDepth = -mvPosition.z;\\n#endif\";\n\n var fog_pars_vertex = \"#ifdef USE_FOG\\n varying float fogDepth;\\n#endif\\n\";\n\n var fog_fragment = \"#ifdef USE_FOG\\n\\t#ifdef FOG_EXP2\\n\\t\\tfloat fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 ) );\\n\\t#else\\n\\t\\tfloat fogFactor = smoothstep( fogNear, fogFar, fogDepth );\\n\\t#endif\\n\\tgl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );\\n#endif\\n\";\n\n var fog_pars_fragment = \"#ifdef USE_FOG\\n\\tuniform vec3 fogColor;\\n\\tvarying float fogDepth;\\n\\t#ifdef FOG_EXP2\\n\\t\\tuniform float fogDensity;\\n\\t#else\\n\\t\\tuniform float fogNear;\\n\\t\\tuniform float fogFar;\\n\\t#endif\\n#endif\\n\";\n\n var gradientmap_pars_fragment = \"#ifdef TOON\\n\\tuniform sampler2D gradientMap;\\n\\tvec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) {\\n\\t\\tfloat dotNL = dot( normal, lightDirection );\\n\\t\\tvec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 );\\n\\t\\t#ifdef USE_GRADIENTMAP\\n\\t\\t\\treturn texture2D( gradientMap, coord ).rgb;\\n\\t\\t#else\\n\\t\\t\\treturn ( coord.x < 0.7 ) ? vec3( 0.7 ) : vec3( 1.0 );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var lightmap_fragment = \"#ifdef USE_LIGHTMAP\\n\\treflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n#endif\\n\";\n\n var lightmap_pars_fragment = \"#ifdef USE_LIGHTMAP\\n\\tuniform sampler2D lightMap;\\n\\tuniform float lightMapIntensity;\\n#endif\";\n\n var lights_lambert_vertex = \"vec3 diffuse = vec3( 1.0 );\\nGeometricContext geometry;\\ngeometry.position = mvPosition.xyz;\\ngeometry.normal = normalize( transformedNormal );\\ngeometry.viewDir = normalize( -mvPosition.xyz );\\nGeometricContext backGeometry;\\nbackGeometry.position = geometry.position;\\nbackGeometry.normal = -geometry.normal;\\nbackGeometry.viewDir = geometry.viewDir;\\nvLightFront = vec3( 0.0 );\\n#ifdef DOUBLE_SIDED\\n\\tvLightBack = vec3( 0.0 );\\n#endif\\nIncidentLight directLight;\\nfloat dotNL;\\nvec3 directLightColor_Diffuse;\\n#if NUM_POINT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tgetPointDirectLightIrradiance( pointLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tgetSpotDirectLightIrradiance( spotLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_DIR_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLights[ i ], geometry, directLight );\\n\\t\\tdotNL = dot( geometry.normal, directLight.direction );\\n\\t\\tdirectLightColor_Diffuse = PI * directLight.color;\\n\\t\\tvLightFront += saturate( dotNL ) * directLightColor_Diffuse;\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\\n\\t\\t#endif\\n\\t}\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\tvLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t#ifdef DOUBLE_SIDED\\n\\t\\t\\tvLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var lights_pars_begin = \"uniform vec3 ambientLightColor;\\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\\n\\tvec3 irradiance = ambientLightColor;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treturn irradiance;\\n}\\n#if NUM_DIR_LIGHTS > 0\\n\\tstruct DirectionalLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\\n\\tvoid getDirectionalDirectLightIrradiance( const in DirectionalLight directionalLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tdirectLight.color = directionalLight.color;\\n\\t\\tdirectLight.direction = directionalLight.direction;\\n\\t\\tdirectLight.visible = true;\\n\\t}\\n#endif\\n#if NUM_POINT_LIGHTS > 0\\n\\tstruct PointLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t\\tfloat shadowCameraNear;\\n\\t\\tfloat shadowCameraFar;\\n\\t};\\n\\tuniform PointLight pointLights[ NUM_POINT_LIGHTS ];\\n\\tvoid getPointDirectLightIrradiance( const in PointLight pointLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = pointLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tdirectLight.color = pointLight.color;\\n\\t\\tdirectLight.color *= punctualLightIntensityToIrradianceFactor( lightDistance, pointLight.distance, pointLight.decay );\\n\\t\\tdirectLight.visible = ( directLight.color != vec3( 0.0 ) );\\n\\t}\\n#endif\\n#if NUM_SPOT_LIGHTS > 0\\n\\tstruct SpotLight {\\n\\t\\tvec3 position;\\n\\t\\tvec3 direction;\\n\\t\\tvec3 color;\\n\\t\\tfloat distance;\\n\\t\\tfloat decay;\\n\\t\\tfloat coneCos;\\n\\t\\tfloat penumbraCos;\\n\\t\\tint shadow;\\n\\t\\tfloat shadowBias;\\n\\t\\tfloat shadowRadius;\\n\\t\\tvec2 shadowMapSize;\\n\\t};\\n\\tuniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\\n\\tvoid getSpotDirectLightIrradiance( const in SpotLight spotLight, const in GeometricContext geometry, out IncidentLight directLight ) {\\n\\t\\tvec3 lVector = spotLight.position - geometry.position;\\n\\t\\tdirectLight.direction = normalize( lVector );\\n\\t\\tfloat lightDistance = length( lVector );\\n\\t\\tfloat angleCos = dot( directLight.direction, spotLight.direction );\\n\\t\\tif ( angleCos > spotLight.coneCos ) {\\n\\t\\t\\tfloat spotEffect = smoothstep( spotLight.coneCos, spotLight.penumbraCos, angleCos );\\n\\t\\t\\tdirectLight.color = spotLight.color;\\n\\t\\t\\tdirectLight.color *= spotEffect * punctualLightIntensityToIrradianceFactor( lightDistance, spotLight.distance, spotLight.decay );\\n\\t\\t\\tdirectLight.visible = true;\\n\\t\\t} else {\\n\\t\\t\\tdirectLight.color = vec3( 0.0 );\\n\\t\\t\\tdirectLight.visible = false;\\n\\t\\t}\\n\\t}\\n#endif\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tstruct RectAreaLight {\\n\\t\\tvec3 color;\\n\\t\\tvec3 position;\\n\\t\\tvec3 halfWidth;\\n\\t\\tvec3 halfHeight;\\n\\t};\\n\\tuniform sampler2D ltc_1;\\tuniform sampler2D ltc_2;\\n\\tuniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ];\\n#endif\\n#if NUM_HEMI_LIGHTS > 0\\n\\tstruct HemisphereLight {\\n\\t\\tvec3 direction;\\n\\t\\tvec3 skyColor;\\n\\t\\tvec3 groundColor;\\n\\t};\\n\\tuniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\\n\\tvec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\\n\\t\\tfloat dotNL = dot( geometry.normal, hemiLight.direction );\\n\\t\\tfloat hemiDiffuseWeight = 0.5 * dotNL + 0.5;\\n\\t\\tvec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tirradiance *= PI;\\n\\t\\t#endif\\n\\t\\treturn irradiance;\\n\\t}\\n#endif\\n\";\n\n var lights_pars_maps = \"#if defined( USE_ENVMAP ) && defined( PHYSICAL )\\n\\tvec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\\n\\t\\tvec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryVec = vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV( queryVec, 1.0 );\\n\\t\\t#else\\n\\t\\t\\tvec4 envMapColor = vec4( 0.0 );\\n\\t\\t#endif\\n\\t\\treturn PI * envMapColor.rgb * envMapIntensity;\\n\\t}\\n\\tfloat getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\tfloat maxMIPLevelScalar = float( maxMIPLevel );\\n\\t\\tfloat desiredMIPLevel = maxMIPLevelScalar + 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );\\n\\t\\treturn clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\\n\\t}\\n\\tvec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\\n\\t\\t#ifdef ENVMAP_MODE_REFLECTION\\n\\t\\t\\tvec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\\n\\t\\t#else\\n\\t\\t\\tvec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\\n\\t\\t#endif\\n\\t\\treflectVec = inverseTransformDirection( reflectVec, viewMatrix );\\n\\t\\tfloat specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\\n\\t\\t#ifdef ENVMAP_TYPE_CUBE\\n\\t\\t\\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\t\\tvec3 queryReflectVec = vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\\n\\t\\t\\tvec4 envMapColor = textureCubeUV(queryReflectVec, BlinnExponentToGGXRoughness(blinnShininessExponent));\\n\\t\\t#elif defined( ENVMAP_TYPE_EQUIREC )\\n\\t\\t\\tvec2 sampleUV;\\n\\t\\t\\tsampleUV.y = asin( clamp( reflectVec.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\t\\t\\tsampleUV.x = atan( reflectVec.z, reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#elif defined( ENVMAP_TYPE_SPHERE )\\n\\t\\t\\tvec3 reflectView = normalize( ( viewMatrix * vec4( reflectVec, 0.0 ) ).xyz + vec3( 0.0,0.0,1.0 ) );\\n\\t\\t\\t#ifdef TEXTURE_LOD_EXT\\n\\t\\t\\t\\tvec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#else\\n\\t\\t\\t\\tvec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\\n\\t\\t\\t#endif\\n\\t\\t\\tenvMapColor.rgb = envMapTexelToLinear( envMapColor ).rgb;\\n\\t\\t#endif\\n\\t\\treturn envMapColor.rgb * envMapIntensity;\\n\\t}\\n#endif\\n\";\n\n var lights_phong_fragment = \"BlinnPhongMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb;\\nmaterial.specularColor = specular;\\nmaterial.specularShininess = shininess;\\nmaterial.specularStrength = specularStrength;\\n\";\n\n var lights_phong_pars_fragment = \"varying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\nstruct BlinnPhongMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tvec3\\tspecularColor;\\n\\tfloat\\tspecularShininess;\\n\\tfloat\\tspecularStrength;\\n};\\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t#ifdef TOON\\n\\t\\tvec3 irradiance = getGradientIrradiance( geometry.normal, directLight.direction ) * directLight.color;\\n\\t#else\\n\\t\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\t\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#endif\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\treflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\treflectedLight.directSpecular += irradiance * BRDF_Specular_BlinnPhong( directLight, geometry, material.specularColor, material.specularShininess ) * material.specularStrength;\\n}\\nvoid RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_BlinnPhong\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_BlinnPhong\\n#define Material_LightProbeLOD( material )\\t(0)\\n\";\n\n var lights_physical_fragment = \"PhysicalMaterial material;\\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\\n#ifdef STANDARD\\n\\tmaterial.specularColor = mix( vec3( DEFAULT_SPECULAR_COEFFICIENT ), diffuseColor.rgb, metalnessFactor );\\n#else\\n\\tmaterial.specularColor = mix( vec3( MAXIMUM_SPECULAR_COEFFICIENT * pow2( reflectivity ) ), diffuseColor.rgb, metalnessFactor );\\n\\tmaterial.clearCoat = saturate( clearCoat );\\tmaterial.clearCoatRoughness = clamp( clearCoatRoughness, 0.04, 1.0 );\\n#endif\\n\";\n\n var lights_physical_pars_fragment = \"struct PhysicalMaterial {\\n\\tvec3\\tdiffuseColor;\\n\\tfloat\\tspecularRoughness;\\n\\tvec3\\tspecularColor;\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoat;\\n\\t\\tfloat clearCoatRoughness;\\n\\t#endif\\n};\\n#define MAXIMUM_SPECULAR_COEFFICIENT 0.16\\n#define DEFAULT_SPECULAR_COEFFICIENT 0.04\\nfloat clearCoatDHRApprox( const in float roughness, const in float dotNL ) {\\n\\treturn DEFAULT_SPECULAR_COEFFICIENT + ( 1.0 - DEFAULT_SPECULAR_COEFFICIENT ) * ( pow( 1.0 - dotNL, 5.0 ) * pow( 1.0 - roughness, 2.0 ) );\\n}\\n#if NUM_RECT_AREA_LIGHTS > 0\\n\\tvoid RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t\\tvec3 normal = geometry.normal;\\n\\t\\tvec3 viewDir = geometry.viewDir;\\n\\t\\tvec3 position = geometry.position;\\n\\t\\tvec3 lightPos = rectAreaLight.position;\\n\\t\\tvec3 halfWidth = rectAreaLight.halfWidth;\\n\\t\\tvec3 halfHeight = rectAreaLight.halfHeight;\\n\\t\\tvec3 lightColor = rectAreaLight.color;\\n\\t\\tfloat roughness = material.specularRoughness;\\n\\t\\tvec3 rectCoords[ 4 ];\\n\\t\\trectCoords[ 0 ] = lightPos - halfWidth - halfHeight;\\t\\trectCoords[ 1 ] = lightPos + halfWidth - halfHeight;\\n\\t\\trectCoords[ 2 ] = lightPos + halfWidth + halfHeight;\\n\\t\\trectCoords[ 3 ] = lightPos - halfWidth + halfHeight;\\n\\t\\tvec2 uv = LTC_Uv( normal, viewDir, roughness );\\n\\t\\tvec4 t1 = texture2D( ltc_1, uv );\\n\\t\\tvec4 t2 = texture2D( ltc_2, uv );\\n\\t\\tmat3 mInv = mat3(\\n\\t\\t\\tvec3( t1.x, 0, t1.y ),\\n\\t\\t\\tvec3( 0, 1, 0 ),\\n\\t\\t\\tvec3( t1.z, 0, t1.w )\\n\\t\\t);\\n\\t\\tvec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );\\n\\t\\treflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );\\n\\t\\treflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );\\n\\t}\\n#endif\\nvoid RE_Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\tfloat dotNL = saturate( dot( geometry.normal, directLight.direction ) );\\n\\tvec3 irradiance = dotNL * directLight.color;\\n\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\tirradiance *= PI;\\n\\t#endif\\n\\t#ifndef STANDARD\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.directSpecular += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\\n\\treflectedLight.directDiffuse += ( 1.0 - clearCoatDHR ) * irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.directSpecular += irradiance * material.clearCoat * BRDF_Specular_GGX( directLight, geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\nvoid RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\treflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\\n}\\nvoid RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 clearCoatRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {\\n\\t#ifndef STANDARD\\n\\t\\tfloat dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\\n\\t\\tfloat dotNL = dotNV;\\n\\t\\tfloat clearCoatDHR = material.clearCoat * clearCoatDHRApprox( material.clearCoatRoughness, dotNL );\\n\\t#else\\n\\t\\tfloat clearCoatDHR = 0.0;\\n\\t#endif\\n\\treflectedLight.indirectSpecular += ( 1.0 - clearCoatDHR ) * radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\\n\\t#ifndef STANDARD\\n\\t\\treflectedLight.indirectSpecular += clearCoatRadiance * material.clearCoat * BRDF_Specular_GGX_Environment( geometry, vec3( DEFAULT_SPECULAR_COEFFICIENT ), material.clearCoatRoughness );\\n\\t#endif\\n}\\n#define RE_Direct\\t\\t\\t\\tRE_Direct_Physical\\n#define RE_Direct_RectArea\\t\\tRE_Direct_RectArea_Physical\\n#define RE_IndirectDiffuse\\t\\tRE_IndirectDiffuse_Physical\\n#define RE_IndirectSpecular\\t\\tRE_IndirectSpecular_Physical\\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\\n#define Material_ClearCoat_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.clearCoatRoughness )\\nfloat computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) {\\n\\treturn saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion );\\n}\\n\";\n\n var lights_fragment_begin = \"\\nGeometricContext geometry;\\ngeometry.position = - vViewPosition;\\ngeometry.normal = normal;\\ngeometry.viewDir = normalize( vViewPosition );\\nIncidentLight directLight;\\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tPointLight pointLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tgetPointDirectLightIrradiance( pointLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tSpotLight spotLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tgetSpotDirectLightIrradiance( spotLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\\n\\tDirectionalLight directionalLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tgetDirectionalDirectLightIrradiance( directionalLight, geometry, directLight );\\n\\t\\t#ifdef USE_SHADOWMAP\\n\\t\\tdirectLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t\\t#endif\\n\\t\\tRE_Direct( directLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea )\\n\\tRectAreaLight rectAreaLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) {\\n\\t\\trectAreaLight = rectAreaLights[ i ];\\n\\t\\tRE_Direct_RectArea( rectAreaLight, geometry, material, reflectedLight );\\n\\t}\\n#endif\\n#if defined( RE_IndirectDiffuse )\\n\\tvec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\\n\\t#if ( NUM_HEMI_LIGHTS > 0 )\\n\\t\\t#pragma unroll_loop\\n\\t\\tfor ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\\n\\t\\t\\tirradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\\n\\t\\t}\\n\\t#endif\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tvec3 radiance = vec3( 0.0 );\\n\\tvec3 clearCoatRadiance = vec3( 0.0 );\\n#endif\\n\";\n\n var lights_fragment_maps = \"#if defined( RE_IndirectDiffuse )\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\tvec3 lightMapIrradiance = texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n\\t\\t#ifndef PHYSICALLY_CORRECT_LIGHTS\\n\\t\\t\\tlightMapIrradiance *= PI;\\n\\t\\t#endif\\n\\t\\tirradiance += lightMapIrradiance;\\n\\t#endif\\n\\t#if defined( USE_ENVMAP ) && defined( PHYSICAL ) && defined( ENVMAP_TYPE_CUBE_UV )\\n\\t\\tirradiance += getLightProbeIndirectIrradiance( geometry, maxMipLevel );\\n\\t#endif\\n#endif\\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\\n\\tradiance += getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), maxMipLevel );\\n\\t#ifndef STANDARD\\n\\t\\tclearCoatRadiance += getLightProbeIndirectRadiance( geometry, Material_ClearCoat_BlinnShininessExponent( material ), maxMipLevel );\\n\\t#endif\\n#endif\\n\";\n\n var lights_fragment_end = \"#if defined( RE_IndirectDiffuse )\\n\\tRE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\\n#endif\\n#if defined( RE_IndirectSpecular )\\n\\tRE_IndirectSpecular( radiance, clearCoatRadiance, geometry, material, reflectedLight );\\n#endif\\n\";\n\n var logdepthbuf_fragment = \"#if defined( USE_LOGDEPTHBUF ) && defined( USE_LOGDEPTHBUF_EXT )\\n\\tgl_FragDepthEXT = log2( vFragDepth ) * logDepthBufFC * 0.5;\\n#endif\";\n\n var logdepthbuf_pars_fragment = \"#ifdef USE_LOGDEPTHBUF\\n\\tuniform float logDepthBufFC;\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n#endif\\n\";\n\n var logdepthbuf_pars_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvarying float vFragDepth;\\n\\t#endif\\n\\tuniform float logDepthBufFC;\\n#endif\";\n\n var logdepthbuf_vertex = \"#ifdef USE_LOGDEPTHBUF\\n\\t#ifdef USE_LOGDEPTHBUF_EXT\\n\\t\\tvFragDepth = 1.0 + gl_Position.w;\\n\\t#else\\n\\t\\tgl_Position.z = log2( max( EPSILON, gl_Position.w + 1.0 ) ) * logDepthBufFC - 1.0;\\n\\t\\tgl_Position.z *= gl_Position.w;\\n\\t#endif\\n#endif\\n\";\n\n var map_fragment = \"#ifdef USE_MAP\\n\\tvec4 texelColor = texture2D( map, vUv );\\n\\ttexelColor = mapTexelToLinear( texelColor );\\n\\tdiffuseColor *= texelColor;\\n#endif\\n\";\n\n var map_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n var map_particle_fragment = \"#ifdef USE_MAP\\n\\tvec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy;\\n\\tvec4 mapTexel = texture2D( map, uv );\\n\\tdiffuseColor *= mapTexelToLinear( mapTexel );\\n#endif\\n\";\n\n var map_particle_pars_fragment = \"#ifdef USE_MAP\\n\\tuniform mat3 uvTransform;\\n\\tuniform sampler2D map;\\n#endif\\n\";\n\n var metalnessmap_fragment = \"float metalnessFactor = metalness;\\n#ifdef USE_METALNESSMAP\\n\\tvec4 texelMetalness = texture2D( metalnessMap, vUv );\\n\\tmetalnessFactor *= texelMetalness.b;\\n#endif\\n\";\n\n var metalnessmap_pars_fragment = \"#ifdef USE_METALNESSMAP\\n\\tuniform sampler2D metalnessMap;\\n#endif\";\n\n var morphnormal_vertex = \"#ifdef USE_MORPHNORMALS\\n\\tobjectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\\n\\tobjectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\\n\\tobjectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\\n\\tobjectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\\n#endif\\n\";\n\n var morphtarget_pars_vertex = \"#ifdef USE_MORPHTARGETS\\n\\t#ifndef USE_MORPHNORMALS\\n\\tuniform float morphTargetInfluences[ 8 ];\\n\\t#else\\n\\tuniform float morphTargetInfluences[ 4 ];\\n\\t#endif\\n#endif\";\n\n var morphtarget_vertex = \"#ifdef USE_MORPHTARGETS\\n\\ttransformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\\n\\ttransformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\\n\\ttransformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\\n\\ttransformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\\n\\t#ifndef USE_MORPHNORMALS\\n\\ttransformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\\n\\ttransformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\\n\\ttransformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\\n\\ttransformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\\n\\t#endif\\n#endif\\n\";\n\n var normal_fragment_begin = \"#ifdef FLAT_SHADED\\n\\tvec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\\n\\tvec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\\n\\tvec3 normal = normalize( cross( fdx, fdy ) );\\n#else\\n\\tvec3 normal = normalize( vNormal );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\tnormal = normal * ( float( gl_FrontFacing ) * 2.0 - 1.0 );\\n\\t#endif\\n#endif\\n\";\n\n var normal_fragment_maps = \"#ifdef USE_NORMALMAP\\n\\tnormal = perturbNormal2Arb( -vViewPosition, normal );\\n#elif defined( USE_BUMPMAP )\\n\\tnormal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\\n#endif\\n\";\n\n var normalmap_pars_fragment = \"#ifdef USE_NORMALMAP\\n\\tuniform sampler2D normalMap;\\n\\tuniform vec2 normalScale;\\n\\tvec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\\n\\t\\tvec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );\\n\\t\\tvec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );\\n\\t\\tvec2 st0 = dFdx( vUv.st );\\n\\t\\tvec2 st1 = dFdy( vUv.st );\\n\\t\\tvec3 S = normalize( q0 * st1.t - q1 * st0.t );\\n\\t\\tvec3 T = normalize( -q0 * st1.s + q1 * st0.s );\\n\\t\\tvec3 N = normalize( surf_norm );\\n\\t\\tvec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\\n\\t\\tmapN.xy = normalScale * mapN.xy;\\n\\t\\tmat3 tsn = mat3( S, T, N );\\n\\t\\treturn normalize( tsn * mapN );\\n\\t}\\n#endif\\n\";\n\n var packing = \"vec3 packNormalToRGB( const in vec3 normal ) {\\n\\treturn normalize( normal ) * 0.5 + 0.5;\\n}\\nvec3 unpackRGBToNormal( const in vec3 rgb ) {\\n\\treturn 2.0 * rgb.xyz - 1.0;\\n}\\nconst float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;\\nconst vec3 PackFactors = vec3( 256. * 256. * 256., 256. * 256., 256. );\\nconst vec4 UnpackFactors = UnpackDownscale / vec4( PackFactors, 1. );\\nconst float ShiftRight8 = 1. / 256.;\\nvec4 packDepthToRGBA( const in float v ) {\\n\\tvec4 r = vec4( fract( v * PackFactors ), v );\\n\\tr.yzw -= r.xyz * ShiftRight8;\\treturn r * PackUpscale;\\n}\\nfloat unpackRGBAToDepth( const in vec4 v ) {\\n\\treturn dot( v, UnpackFactors );\\n}\\nfloat viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn ( viewZ + near ) / ( near - far );\\n}\\nfloat orthographicDepthToViewZ( const in float linearClipZ, const in float near, const in float far ) {\\n\\treturn linearClipZ * ( near - far ) - near;\\n}\\nfloat viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) {\\n\\treturn (( near + viewZ ) * far ) / (( far - near ) * viewZ );\\n}\\nfloat perspectiveDepthToViewZ( const in float invClipZ, const in float near, const in float far ) {\\n\\treturn ( near * far ) / ( ( far - near ) * invClipZ - far );\\n}\\n\";\n\n var premultiplied_alpha_fragment = \"#ifdef PREMULTIPLIED_ALPHA\\n\\tgl_FragColor.rgb *= gl_FragColor.a;\\n#endif\\n\";\n\n var project_vertex = \"vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\\ngl_Position = projectionMatrix * mvPosition;\\n\";\n\n var dithering_fragment = \"#if defined( DITHERING )\\n gl_FragColor.rgb = dithering( gl_FragColor.rgb );\\n#endif\\n\";\n\n var dithering_pars_fragment = \"#if defined( DITHERING )\\n\\tvec3 dithering( vec3 color ) {\\n\\t\\tfloat grid_position = rand( gl_FragCoord.xy );\\n\\t\\tvec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 );\\n\\t\\tdither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position );\\n\\t\\treturn color + dither_shift_RGB;\\n\\t}\\n#endif\\n\";\n\n var roughnessmap_fragment = \"float roughnessFactor = roughness;\\n#ifdef USE_ROUGHNESSMAP\\n\\tvec4 texelRoughness = texture2D( roughnessMap, vUv );\\n\\troughnessFactor *= texelRoughness.g;\\n#endif\\n\";\n\n var roughnessmap_pars_fragment = \"#ifdef USE_ROUGHNESSMAP\\n\\tuniform sampler2D roughnessMap;\\n#endif\";\n\n var shadowmap_pars_fragment = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n\\tfloat texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\\n\\t\\treturn step( compare, unpackRGBAToDepth( texture2D( depths, uv ) ) );\\n\\t}\\n\\tfloat texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\\n\\t\\tconst vec2 offset = vec2( 0.0, 1.0 );\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / size;\\n\\t\\tvec2 centroidUV = floor( uv * size + 0.5 ) / size;\\n\\t\\tfloat lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\\n\\t\\tfloat lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\\n\\t\\tfloat rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\\n\\t\\tfloat rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\\n\\t\\tvec2 f = fract( uv * size + 0.5 );\\n\\t\\tfloat a = mix( lb, lt, f.y );\\n\\t\\tfloat b = mix( rb, rt, f.y );\\n\\t\\tfloat c = mix( a, b, f.x );\\n\\t\\treturn c;\\n\\t}\\n\\tfloat getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\\n\\t\\tfloat shadow = 1.0;\\n\\t\\tshadowCoord.xyz /= shadowCoord.w;\\n\\t\\tshadowCoord.z += shadowBias;\\n\\t\\tbvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\\n\\t\\tbool inFrustum = all( inFrustumVec );\\n\\t\\tbvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\\n\\t\\tbool frustumTest = all( frustumTestVec );\\n\\t\\tif ( frustumTest ) {\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#elif defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 texelSize = vec2( 1.0 ) / shadowMapSize;\\n\\t\\t\\tfloat dx0 = - texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy0 = - texelSize.y * shadowRadius;\\n\\t\\t\\tfloat dx1 = + texelSize.x * shadowRadius;\\n\\t\\t\\tfloat dy1 = + texelSize.y * shadowRadius;\\n\\t\\t\\tshadow = (\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\\n\\t\\t\\t\\ttexture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\tshadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\\n\\t\\t#endif\\n\\t\\t}\\n\\t\\treturn shadow;\\n\\t}\\n\\tvec2 cubeToUV( vec3 v, float texelSizeY ) {\\n\\t\\tvec3 absV = abs( v );\\n\\t\\tfloat scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\\n\\t\\tabsV *= scaleToCube;\\n\\t\\tv *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\\n\\t\\tvec2 planar = v.xy;\\n\\t\\tfloat almostATexel = 1.5 * texelSizeY;\\n\\t\\tfloat almostOne = 1.0 - almostATexel;\\n\\t\\tif ( absV.z >= almostOne ) {\\n\\t\\t\\tif ( v.z > 0.0 )\\n\\t\\t\\t\\tplanar.x = 4.0 - v.x;\\n\\t\\t} else if ( absV.x >= almostOne ) {\\n\\t\\t\\tfloat signX = sign( v.x );\\n\\t\\t\\tplanar.x = v.z * signX + 2.0 * signX;\\n\\t\\t} else if ( absV.y >= almostOne ) {\\n\\t\\t\\tfloat signY = sign( v.y );\\n\\t\\t\\tplanar.x = v.x + 2.0 * signY + 2.0;\\n\\t\\t\\tplanar.y = v.z * signY - 2.0;\\n\\t\\t}\\n\\t\\treturn vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\\n\\t}\\n\\tfloat getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) {\\n\\t\\tvec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\\n\\t\\tvec3 lightToPosition = shadowCoord.xyz;\\n\\t\\tfloat dp = ( length( lightToPosition ) - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear );\\t\\tdp += shadowBias;\\n\\t\\tvec3 bd3D = normalize( lightToPosition );\\n\\t\\t#if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\\n\\t\\t\\tvec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\\n\\t\\t\\treturn (\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\\n\\t\\t\\t\\ttexture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\\n\\t\\t\\t) * ( 1.0 / 9.0 );\\n\\t\\t#else\\n\\t\\t\\treturn texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\\n\\t\\t#endif\\n\\t}\\n#endif\\n\";\n\n var shadowmap_pars_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t\\tuniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\\n\\t\\tvarying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t\\tuniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\\n\\t\\tvarying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t\\tuniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\\n\\t\\tvarying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\\n\\t#endif\\n#endif\\n\";\n\n var shadowmap_vertex = \"#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tvDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tvSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tvPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\\n\\t}\\n\\t#endif\\n#endif\\n\";\n\n var shadowmask_pars_fragment = \"float getShadowMask() {\\n\\tfloat shadow = 1.0;\\n\\t#ifdef USE_SHADOWMAP\\n\\t#if NUM_DIR_LIGHTS > 0\\n\\tDirectionalLight directionalLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\\n\\t\\tdirectionalLight = directionalLights[ i ];\\n\\t\\tshadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_SPOT_LIGHTS > 0\\n\\tSpotLight spotLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\\n\\t\\tspotLight = spotLights[ i ];\\n\\t\\tshadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#if NUM_POINT_LIGHTS > 0\\n\\tPointLight pointLight;\\n\\t#pragma unroll_loop\\n\\tfor ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\\n\\t\\tpointLight = pointLights[ i ];\\n\\t\\tshadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0;\\n\\t}\\n\\t#endif\\n\\t#endif\\n\\treturn shadow;\\n}\\n\";\n\n var skinbase_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 boneMatX = getBoneMatrix( skinIndex.x );\\n\\tmat4 boneMatY = getBoneMatrix( skinIndex.y );\\n\\tmat4 boneMatZ = getBoneMatrix( skinIndex.z );\\n\\tmat4 boneMatW = getBoneMatrix( skinIndex.w );\\n#endif\";\n\n var skinning_pars_vertex = \"#ifdef USE_SKINNING\\n\\tuniform mat4 bindMatrix;\\n\\tuniform mat4 bindMatrixInverse;\\n\\t#ifdef BONE_TEXTURE\\n\\t\\tuniform sampler2D boneTexture;\\n\\t\\tuniform int boneTextureSize;\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tfloat j = i * 4.0;\\n\\t\\t\\tfloat x = mod( j, float( boneTextureSize ) );\\n\\t\\t\\tfloat y = floor( j / float( boneTextureSize ) );\\n\\t\\t\\tfloat dx = 1.0 / float( boneTextureSize );\\n\\t\\t\\tfloat dy = 1.0 / float( boneTextureSize );\\n\\t\\t\\ty = dy * ( y + 0.5 );\\n\\t\\t\\tvec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\\n\\t\\t\\tvec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\\n\\t\\t\\tvec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\\n\\t\\t\\tvec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\\n\\t\\t\\tmat4 bone = mat4( v1, v2, v3, v4 );\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#else\\n\\t\\tuniform mat4 boneMatrices[ MAX_BONES ];\\n\\t\\tmat4 getBoneMatrix( const in float i ) {\\n\\t\\t\\tmat4 bone = boneMatrices[ int(i) ];\\n\\t\\t\\treturn bone;\\n\\t\\t}\\n\\t#endif\\n#endif\\n\";\n\n var skinning_vertex = \"#ifdef USE_SKINNING\\n\\tvec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\\n\\tvec4 skinned = vec4( 0.0 );\\n\\tskinned += boneMatX * skinVertex * skinWeight.x;\\n\\tskinned += boneMatY * skinVertex * skinWeight.y;\\n\\tskinned += boneMatZ * skinVertex * skinWeight.z;\\n\\tskinned += boneMatW * skinVertex * skinWeight.w;\\n\\ttransformed = ( bindMatrixInverse * skinned ).xyz;\\n#endif\\n\";\n\n var skinnormal_vertex = \"#ifdef USE_SKINNING\\n\\tmat4 skinMatrix = mat4( 0.0 );\\n\\tskinMatrix += skinWeight.x * boneMatX;\\n\\tskinMatrix += skinWeight.y * boneMatY;\\n\\tskinMatrix += skinWeight.z * boneMatZ;\\n\\tskinMatrix += skinWeight.w * boneMatW;\\n\\tskinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\\n\\tobjectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\\n#endif\\n\";\n\n var specularmap_fragment = \"float specularStrength;\\n#ifdef USE_SPECULARMAP\\n\\tvec4 texelSpecular = texture2D( specularMap, vUv );\\n\\tspecularStrength = texelSpecular.r;\\n#else\\n\\tspecularStrength = 1.0;\\n#endif\";\n\n var specularmap_pars_fragment = \"#ifdef USE_SPECULARMAP\\n\\tuniform sampler2D specularMap;\\n#endif\";\n\n var tonemapping_fragment = \"#if defined( TONE_MAPPING )\\n gl_FragColor.rgb = toneMapping( gl_FragColor.rgb );\\n#endif\\n\";\n\n var tonemapping_pars_fragment = \"#ifndef saturate\\n\\t#define saturate(a) clamp( a, 0.0, 1.0 )\\n#endif\\nuniform float toneMappingExposure;\\nuniform float toneMappingWhitePoint;\\nvec3 LinearToneMapping( vec3 color ) {\\n\\treturn toneMappingExposure * color;\\n}\\nvec3 ReinhardToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\treturn saturate( color / ( vec3( 1.0 ) + color ) );\\n}\\n#define Uncharted2Helper( x ) max( ( ( x * ( 0.15 * x + 0.10 * 0.50 ) + 0.20 * 0.02 ) / ( x * ( 0.15 * x + 0.50 ) + 0.20 * 0.30 ) ) - 0.02 / 0.30, vec3( 0.0 ) )\\nvec3 Uncharted2ToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\treturn saturate( Uncharted2Helper( color ) / Uncharted2Helper( vec3( toneMappingWhitePoint ) ) );\\n}\\nvec3 OptimizedCineonToneMapping( vec3 color ) {\\n\\tcolor *= toneMappingExposure;\\n\\tcolor = max( vec3( 0.0 ), color - 0.004 );\\n\\treturn pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) );\\n}\\n\";\n\n var uv_pars_fragment = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n#endif\";\n\n var uv_pars_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvarying vec2 vUv;\\n\\tuniform mat3 uvTransform;\\n#endif\\n\";\n\n var uv_vertex = \"#if defined( USE_MAP ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) || defined( USE_ALPHAMAP ) || defined( USE_EMISSIVEMAP ) || defined( USE_ROUGHNESSMAP ) || defined( USE_METALNESSMAP )\\n\\tvUv = ( uvTransform * vec3( uv, 1 ) ).xy;\\n#endif\";\n\n var uv2_pars_fragment = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n var uv2_pars_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tattribute vec2 uv2;\\n\\tvarying vec2 vUv2;\\n#endif\";\n\n var uv2_vertex = \"#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\\n\\tvUv2 = uv2;\\n#endif\";\n\n var worldpos_vertex = \"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP )\\n\\tvec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\\n#endif\\n\";\n\n var cube_frag = \"uniform samplerCube tCube;\\nuniform float tFlip;\\nuniform float opacity;\\nvarying vec3 vWorldPosition;\\nvoid main() {\\n\\tgl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );\\n\\tgl_FragColor.a *= opacity;\\n}\\n\";\n\n var cube_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\tgl_Position.z = gl_Position.w;\\n}\\n\";\n\n var depth_frag = \"#if DEPTH_PACKING == 3200\\n\\tuniform float opacity;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tdiffuseColor.a = opacity;\\n\\t#endif\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <logdepthbuf_fragment>\\n\\t#if DEPTH_PACKING == 3200\\n\\t\\tgl_FragColor = vec4( vec3( 1.0 - gl_FragCoord.z ), opacity );\\n\\t#elif DEPTH_PACKING == 3201\\n\\t\\tgl_FragColor = packDepthToRGBA( gl_FragCoord.z );\\n\\t#endif\\n}\\n\";\n\n var depth_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n}\\n\";\n\n var distanceRGBA_frag = \"#define DISTANCE\\nuniform vec3 referencePosition;\\nuniform float nearDistance;\\nuniform float farDistance;\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main () {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( 1.0 );\\n\\t#include <map_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\tfloat dist = length( vWorldPosition - referencePosition );\\n\\tdist = ( dist - nearDistance ) / ( farDistance - nearDistance );\\n\\tdist = saturate( dist );\\n\\tgl_FragColor = packDepthToRGBA( dist );\\n}\\n\";\n\n var distanceRGBA_vert = \"#define DISTANCE\\nvarying vec3 vWorldPosition;\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_DISPLACEMENTMAP\\n\\t\\t#include <beginnormal_vertex>\\n\\t\\t#include <morphnormal_vertex>\\n\\t\\t#include <skinnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvWorldPosition = worldPosition.xyz;\\n}\\n\";\n\n var equirect_frag = \"uniform sampler2D tEquirect;\\nvarying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvec3 direction = normalize( vWorldPosition );\\n\\tvec2 sampleUV;\\n\\tsampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5;\\n\\tsampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;\\n\\tgl_FragColor = texture2D( tEquirect, sampleUV );\\n}\\n\";\n\n var equirect_vert = \"varying vec3 vWorldPosition;\\n#include <common>\\nvoid main() {\\n\\tvWorldPosition = transformDirection( position, modelMatrix );\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n}\\n\";\n\n var linedashed_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\nuniform float dashSize;\\nuniform float totalSize;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tif ( mod( vLineDistance, totalSize ) > dashSize ) {\\n\\t\\tdiscard;\\n\\t}\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <color_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var linedashed_vert = \"uniform float scale;\\nattribute float lineDistance;\\nvarying float vLineDistance;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\tvLineDistance = scale * lineDistance;\\n\\tvec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\\n\\tgl_Position = projectionMatrix * mvPosition;\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshbasic_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\t#ifdef USE_LIGHTMAP\\n\\t\\treflectedLight.indirectDiffuse += texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\\n\\t#else\\n\\t\\treflectedLight.indirectDiffuse += vec3( 1.0 );\\n\\t#endif\\n\\t#include <aomap_fragment>\\n\\treflectedLight.indirectDiffuse *= diffuseColor.rgb;\\n\\tvec3 outgoingLight = reflectedLight.indirectDiffuse;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var meshbasic_vert = \"#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#ifdef USE_ENVMAP\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshlambert_frag = \"uniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float opacity;\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <emissivemap_fragment>\\n\\treflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );\\n\\t#include <lightmap_fragment>\\n\\treflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );\\n\\t#ifdef DOUBLE_SIDED\\n\\t\\treflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;\\n\\t#else\\n\\t\\treflectedLight.directDiffuse = vLightFront;\\n\\t#endif\\n\\treflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshlambert_vert = \"#define LAMBERT\\nvarying vec3 vLightFront;\\n#ifdef DOUBLE_SIDED\\n\\tvarying vec3 vLightBack;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <lights_lambert_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshphong_frag = \"#define PHONG\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform vec3 specular;\\nuniform float shininess;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <gradientmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <lights_phong_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <specularmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <specularmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_phong_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\t#include <envmap_fragment>\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshphong_vert = \"#define PHONG\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <envmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <envmap_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var meshphysical_frag = \"#define PHYSICAL\\nuniform vec3 diffuse;\\nuniform vec3 emissive;\\nuniform float roughness;\\nuniform float metalness;\\nuniform float opacity;\\n#ifndef STANDARD\\n\\tuniform float clearCoat;\\n\\tuniform float clearCoatRoughness;\\n#endif\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <packing>\\n#include <dithering_pars_fragment>\\n#include <color_pars_fragment>\\n#include <uv_pars_fragment>\\n#include <uv2_pars_fragment>\\n#include <map_pars_fragment>\\n#include <alphamap_pars_fragment>\\n#include <aomap_pars_fragment>\\n#include <lightmap_pars_fragment>\\n#include <emissivemap_pars_fragment>\\n#include <envmap_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <cube_uv_reflection_fragment>\\n#include <lights_pars_begin>\\n#include <lights_pars_maps>\\n#include <lights_physical_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <roughnessmap_pars_fragment>\\n#include <metalnessmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\tReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );\\n\\tvec3 totalEmissiveRadiance = emissive;\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphamap_fragment>\\n\\t#include <alphatest_fragment>\\n\\t#include <roughnessmap_fragment>\\n\\t#include <metalnessmap_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\t#include <emissivemap_fragment>\\n\\t#include <lights_physical_fragment>\\n\\t#include <lights_fragment_begin>\\n\\t#include <lights_fragment_maps>\\n\\t#include <lights_fragment_end>\\n\\t#include <aomap_fragment>\\n\\tvec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <dithering_fragment>\\n}\\n\";\n\n var meshphysical_vert = \"#define PHYSICAL\\nvarying vec3 vViewPosition;\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <common>\\n#include <uv_pars_vertex>\\n#include <uv2_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <uv2_vertex>\\n\\t#include <color_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\tvViewPosition = - mvPosition.xyz;\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var normal_frag = \"#define NORMAL\\nuniform float opacity;\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <packing>\\n#include <uv_pars_fragment>\\n#include <bumpmap_pars_fragment>\\n#include <normalmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\nvoid main() {\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <normal_fragment_begin>\\n\\t#include <normal_fragment_maps>\\n\\tgl_FragColor = vec4( packNormalToRGB( normal ), opacity );\\n}\\n\";\n\n var normal_vert = \"#define NORMAL\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvarying vec3 vViewPosition;\\n#endif\\n#ifndef FLAT_SHADED\\n\\tvarying vec3 vNormal;\\n#endif\\n#include <uv_pars_vertex>\\n#include <displacementmap_pars_vertex>\\n#include <morphtarget_pars_vertex>\\n#include <skinning_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\nvoid main() {\\n\\t#include <uv_vertex>\\n\\t#include <beginnormal_vertex>\\n\\t#include <morphnormal_vertex>\\n\\t#include <skinbase_vertex>\\n\\t#include <skinnormal_vertex>\\n\\t#include <defaultnormal_vertex>\\n#ifndef FLAT_SHADED\\n\\tvNormal = normalize( transformedNormal );\\n#endif\\n\\t#include <begin_vertex>\\n\\t#include <morphtarget_vertex>\\n\\t#include <skinning_vertex>\\n\\t#include <displacementmap_vertex>\\n\\t#include <project_vertex>\\n\\t#include <logdepthbuf_vertex>\\n#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP )\\n\\tvViewPosition = - mvPosition.xyz;\\n#endif\\n}\\n\";\n\n var points_frag = \"uniform vec3 diffuse;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <color_pars_fragment>\\n#include <map_particle_pars_fragment>\\n#include <fog_pars_fragment>\\n#include <shadowmap_pars_fragment>\\n#include <logdepthbuf_pars_fragment>\\n#include <clipping_planes_pars_fragment>\\nvoid main() {\\n\\t#include <clipping_planes_fragment>\\n\\tvec3 outgoingLight = vec3( 0.0 );\\n\\tvec4 diffuseColor = vec4( diffuse, opacity );\\n\\t#include <logdepthbuf_fragment>\\n\\t#include <map_particle_fragment>\\n\\t#include <color_fragment>\\n\\t#include <alphatest_fragment>\\n\\toutgoingLight = diffuseColor.rgb;\\n\\tgl_FragColor = vec4( outgoingLight, diffuseColor.a );\\n\\t#include <premultiplied_alpha_fragment>\\n\\t#include <tonemapping_fragment>\\n\\t#include <encodings_fragment>\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var points_vert = \"uniform float size;\\nuniform float scale;\\n#include <common>\\n#include <color_pars_vertex>\\n#include <fog_pars_vertex>\\n#include <shadowmap_pars_vertex>\\n#include <logdepthbuf_pars_vertex>\\n#include <clipping_planes_pars_vertex>\\nvoid main() {\\n\\t#include <color_vertex>\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#ifdef USE_SIZEATTENUATION\\n\\t\\tgl_PointSize = size * ( scale / - mvPosition.z );\\n\\t#else\\n\\t\\tgl_PointSize = size;\\n\\t#endif\\n\\t#include <logdepthbuf_vertex>\\n\\t#include <clipping_planes_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var shadow_frag = \"uniform vec3 color;\\nuniform float opacity;\\n#include <common>\\n#include <packing>\\n#include <fog_pars_fragment>\\n#include <bsdfs>\\n#include <lights_pars_begin>\\n#include <shadowmap_pars_fragment>\\n#include <shadowmask_pars_fragment>\\nvoid main() {\\n\\tgl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) );\\n\\t#include <fog_fragment>\\n}\\n\";\n\n var shadow_vert = \"#include <fog_pars_vertex>\\n#include <shadowmap_pars_vertex>\\nvoid main() {\\n\\t#include <begin_vertex>\\n\\t#include <project_vertex>\\n\\t#include <worldpos_vertex>\\n\\t#include <shadowmap_vertex>\\n\\t#include <fog_vertex>\\n}\\n\";\n\n var ShaderChunk = {\n alphamap_fragment: alphamap_fragment,\n alphamap_pars_fragment: alphamap_pars_fragment,\n alphatest_fragment: alphatest_fragment,\n aomap_fragment: aomap_fragment,\n aomap_pars_fragment: aomap_pars_fragment,\n begin_vertex: begin_vertex,\n beginnormal_vertex: beginnormal_vertex,\n bsdfs: bsdfs,\n bumpmap_pars_fragment: bumpmap_pars_fragment,\n clipping_planes_fragment: clipping_planes_fragment,\n clipping_planes_pars_fragment: clipping_planes_pars_fragment,\n clipping_planes_pars_vertex: clipping_planes_pars_vertex,\n clipping_planes_vertex: clipping_planes_vertex,\n color_fragment: color_fragment,\n color_pars_fragment: color_pars_fragment,\n color_pars_vertex: color_pars_vertex,\n color_vertex: color_vertex,\n common: common,\n cube_uv_reflection_fragment: cube_uv_reflection_fragment,\n defaultnormal_vertex: defaultnormal_vertex,\n displacementmap_pars_vertex: displacementmap_pars_vertex,\n displacementmap_vertex: displacementmap_vertex,\n emissivemap_fragment: emissivemap_fragment,\n emissivemap_pars_fragment: emissivemap_pars_fragment,\n encodings_fragment: encodings_fragment,\n encodings_pars_fragment: encodings_pars_fragment,\n envmap_fragment: envmap_fragment,\n envmap_pars_fragment: envmap_pars_fragment,\n envmap_pars_vertex: envmap_pars_vertex,\n envmap_vertex: envmap_vertex,\n fog_vertex: fog_vertex,\n fog_pars_vertex: fog_pars_vertex,\n fog_fragment: fog_fragment,\n fog_pars_fragment: fog_pars_fragment,\n gradientmap_pars_fragment: gradientmap_pars_fragment,\n lightmap_fragment: lightmap_fragment,\n lightmap_pars_fragment: lightmap_pars_fragment,\n lights_lambert_vertex: lights_lambert_vertex,\n lights_pars_begin: lights_pars_begin,\n lights_pars_maps: lights_pars_maps,\n lights_phong_fragment: lights_phong_fragment,\n lights_phong_pars_fragment: lights_phong_pars_fragment,\n lights_physical_fragment: lights_physical_fragment,\n lights_physical_pars_fragment: lights_physical_pars_fragment,\n lights_fragment_begin: lights_fragment_begin,\n lights_fragment_maps: lights_fragment_maps,\n lights_fragment_end: lights_fragment_end,\n logdepthbuf_fragment: logdepthbuf_fragment,\n logdepthbuf_pars_fragment: logdepthbuf_pars_fragment,\n logdepthbuf_pars_vertex: logdepthbuf_pars_vertex,\n logdepthbuf_vertex: logdepthbuf_vertex,\n map_fragment: map_fragment,\n map_pars_fragment: map_pars_fragment,\n map_particle_fragment: map_particle_fragment,\n map_particle_pars_fragment: map_particle_pars_fragment,\n metalnessmap_fragment: metalnessmap_fragment,\n metalnessmap_pars_fragment: metalnessmap_pars_fragment,\n morphnormal_vertex: morphnormal_vertex,\n morphtarget_pars_vertex: morphtarget_pars_vertex,\n morphtarget_vertex: morphtarget_vertex,\n normal_fragment_begin: normal_fragment_begin,\n normal_fragment_maps: normal_fragment_maps,\n normalmap_pars_fragment: normalmap_pars_fragment,\n packing: packing,\n premultiplied_alpha_fragment: premultiplied_alpha_fragment,\n project_vertex: project_vertex,\n dithering_fragment: dithering_fragment,\n dithering_pars_fragment: dithering_pars_fragment,\n roughnessmap_fragment: roughnessmap_fragment,\n roughnessmap_pars_fragment: roughnessmap_pars_fragment,\n shadowmap_pars_fragment: shadowmap_pars_fragment,\n shadowmap_pars_vertex: shadowmap_pars_vertex,\n shadowmap_vertex: shadowmap_vertex,\n shadowmask_pars_fragment: shadowmask_pars_fragment,\n skinbase_vertex: skinbase_vertex,\n skinning_pars_vertex: skinning_pars_vertex,\n skinning_vertex: skinning_vertex,\n skinnormal_vertex: skinnormal_vertex,\n specularmap_fragment: specularmap_fragment,\n specularmap_pars_fragment: specularmap_pars_fragment,\n tonemapping_fragment: tonemapping_fragment,\n tonemapping_pars_fragment: tonemapping_pars_fragment,\n uv_pars_fragment: uv_pars_fragment,\n uv_pars_vertex: uv_pars_vertex,\n uv_vertex: uv_vertex,\n uv2_pars_fragment: uv2_pars_fragment,\n uv2_pars_vertex: uv2_pars_vertex,\n uv2_vertex: uv2_vertex,\n worldpos_vertex: worldpos_vertex,\n\n cube_frag: cube_frag,\n cube_vert: cube_vert,\n depth_frag: depth_frag,\n depth_vert: depth_vert,\n distanceRGBA_frag: distanceRGBA_frag,\n distanceRGBA_vert: distanceRGBA_vert,\n equirect_frag: equirect_frag,\n equirect_vert: equirect_vert,\n linedashed_frag: linedashed_frag,\n linedashed_vert: linedashed_vert,\n meshbasic_frag: meshbasic_frag,\n meshbasic_vert: meshbasic_vert,\n meshlambert_frag: meshlambert_frag,\n meshlambert_vert: meshlambert_vert,\n meshphong_frag: meshphong_frag,\n meshphong_vert: meshphong_vert,\n meshphysical_frag: meshphysical_frag,\n meshphysical_vert: meshphysical_vert,\n normal_frag: normal_frag,\n normal_vert: normal_vert,\n points_frag: points_frag,\n points_vert: points_vert,\n shadow_frag: shadow_frag,\n shadow_vert: shadow_vert\n };\n\n /**\n * Uniform Utilities\n */\n\n var UniformsUtils = {\n\n merge: function ( uniforms ) {\n\n var merged = {};\n\n for ( var u = 0; u < uniforms.length; u ++ ) {\n\n var tmp = this.clone( uniforms[ u ] );\n\n for ( var p in tmp ) {\n\n merged[ p ] = tmp[ p ];\n\n }\n\n }\n\n return merged;\n\n },\n\n clone: function ( uniforms_src ) {\n\n var uniforms_dst = {};\n\n for ( var u in uniforms_src ) {\n\n uniforms_dst[ u ] = {};\n\n for ( var p in uniforms_src[ u ] ) {\n\n var parameter_src = uniforms_src[ u ][ p ];\n\n if ( parameter_src && ( parameter_src.isColor ||\n parameter_src.isMatrix3 || parameter_src.isMatrix4 ||\n parameter_src.isVector2 || parameter_src.isVector3 || parameter_src.isVector4 ||\n parameter_src.isTexture ) ) {\n\n uniforms_dst[ u ][ p ] = parameter_src.clone();\n\n } else if ( Array.isArray( parameter_src ) ) {\n\n uniforms_dst[ u ][ p ] = parameter_src.slice();\n\n } else {\n\n uniforms_dst[ u ][ p ] = parameter_src;\n\n }\n\n }\n\n }\n\n return uniforms_dst;\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF,\n 'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2,\n 'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50,\n 'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B,\n 'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B,\n 'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F,\n 'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3,\n 'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222,\n 'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700,\n 'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4,\n 'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00,\n 'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3,\n 'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA,\n 'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32,\n 'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3,\n 'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC,\n 'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD,\n 'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6,\n 'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9,\n 'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'rebeccapurple': 0x663399, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F,\n 'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE,\n 'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA,\n 'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0,\n 'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 };\n\n function Color( r, g, b ) {\n\n if ( g === undefined && b === undefined ) {\n\n // r is THREE.Color, hex or string\n return this.set( r );\n\n }\n\n return this.setRGB( r, g, b );\n\n }\n\n Object.assign( Color.prototype, {\n\n isColor: true,\n\n r: 1, g: 1, b: 1,\n\n set: function ( value ) {\n\n if ( value && value.isColor ) {\n\n this.copy( value );\n\n } else if ( typeof value === 'number' ) {\n\n this.setHex( value );\n\n } else if ( typeof value === 'string' ) {\n\n this.setStyle( value );\n\n }\n\n return this;\n\n },\n\n setScalar: function ( scalar ) {\n\n this.r = scalar;\n this.g = scalar;\n this.b = scalar;\n\n return this;\n\n },\n\n setHex: function ( hex ) {\n\n hex = Math.floor( hex );\n\n this.r = ( hex >> 16 & 255 ) / 255;\n this.g = ( hex >> 8 & 255 ) / 255;\n this.b = ( hex & 255 ) / 255;\n\n return this;\n\n },\n\n setRGB: function ( r, g, b ) {\n\n this.r = r;\n this.g = g;\n this.b = b;\n\n return this;\n\n },\n\n setHSL: function () {\n\n function hue2rgb( p, q, t ) {\n\n if ( t < 0 ) t += 1;\n if ( t > 1 ) t -= 1;\n if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t;\n if ( t < 1 / 2 ) return q;\n if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t );\n return p;\n\n }\n\n return function setHSL( h, s, l ) {\n\n // h,s,l ranges are in 0.0 - 1.0\n h = _Math.euclideanModulo( h, 1 );\n s = _Math.clamp( s, 0, 1 );\n l = _Math.clamp( l, 0, 1 );\n\n if ( s === 0 ) {\n\n this.r = this.g = this.b = l;\n\n } else {\n\n var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s );\n var q = ( 2 * l ) - p;\n\n this.r = hue2rgb( q, p, h + 1 / 3 );\n this.g = hue2rgb( q, p, h );\n this.b = hue2rgb( q, p, h - 1 / 3 );\n\n }\n\n return this;\n\n };\n\n }(),\n\n setStyle: function ( style ) {\n\n function handleAlpha( string ) {\n\n if ( string === undefined ) return;\n\n if ( parseFloat( string ) < 1 ) {\n\n console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' );\n\n }\n\n }\n\n\n var m;\n\n if ( m = /^((?:rgb|hsl)a?)\\(\\s*([^\\)]*)\\)/.exec( style ) ) {\n\n // rgb / hsl\n\n var color;\n var name = m[ 1 ];\n var components = m[ 2 ];\n\n switch ( name ) {\n\n case 'rgb':\n case 'rgba':\n\n if ( color = /^(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // rgb(255,0,0) rgba(255,0,0,0.5)\n this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255;\n this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255;\n this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255;\n\n handleAlpha( color[ 5 ] );\n\n return this;\n\n }\n\n if ( color = /^(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // rgb(100%,0%,0%) rgba(100%,0%,0%,0.5)\n this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100;\n this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100;\n this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100;\n\n handleAlpha( color[ 5 ] );\n\n return this;\n\n }\n\n break;\n\n case 'hsl':\n case 'hsla':\n\n if ( color = /^([0-9]*\\.?[0-9]+)\\s*,\\s*(\\d+)\\%\\s*,\\s*(\\d+)\\%\\s*(,\\s*([0-9]*\\.?[0-9]+)\\s*)?$/.exec( components ) ) {\n\n // hsl(120,50%,50%) hsla(120,50%,50%,0.5)\n var h = parseFloat( color[ 1 ] ) / 360;\n var s = parseInt( color[ 2 ], 10 ) / 100;\n var l = parseInt( color[ 3 ], 10 ) / 100;\n\n handleAlpha( color[ 5 ] );\n\n return this.setHSL( h, s, l );\n\n }\n\n break;\n\n }\n\n } else if ( m = /^\\#([A-Fa-f0-9]+)$/.exec( style ) ) {\n\n // hex color\n\n var hex = m[ 1 ];\n var size = hex.length;\n\n if ( size === 3 ) {\n\n // #ff0\n this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255;\n this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255;\n this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255;\n\n return this;\n\n } else if ( size === 6 ) {\n\n // #ff0000\n this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255;\n this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255;\n this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255;\n\n return this;\n\n }\n\n }\n\n if ( style && style.length > 0 ) {\n\n // color keywords\n var hex = ColorKeywords[ style ];\n\n if ( hex !== undefined ) {\n\n // red\n this.setHex( hex );\n\n } else {\n\n // unknown color\n console.warn( 'THREE.Color: Unknown color ' + style );\n\n }\n\n }\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this.r, this.g, this.b );\n\n },\n\n copy: function ( color ) {\n\n this.r = color.r;\n this.g = color.g;\n this.b = color.b;\n\n return this;\n\n },\n\n copyGammaToLinear: function ( color, gammaFactor ) {\n\n if ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n this.r = Math.pow( color.r, gammaFactor );\n this.g = Math.pow( color.g, gammaFactor );\n this.b = Math.pow( color.b, gammaFactor );\n\n return this;\n\n },\n\n copyLinearToGamma: function ( color, gammaFactor ) {\n\n if ( gammaFactor === undefined ) gammaFactor = 2.0;\n\n var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0;\n\n this.r = Math.pow( color.r, safeInverse );\n this.g = Math.pow( color.g, safeInverse );\n this.b = Math.pow( color.b, safeInverse );\n\n return this;\n\n },\n\n convertGammaToLinear: function () {\n\n var r = this.r, g = this.g, b = this.b;\n\n this.r = r * r;\n this.g = g * g;\n this.b = b * b;\n\n return this;\n\n },\n\n convertLinearToGamma: function () {\n\n this.r = Math.sqrt( this.r );\n this.g = Math.sqrt( this.g );\n this.b = Math.sqrt( this.b );\n\n return this;\n\n },\n\n getHex: function () {\n\n return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0;\n\n },\n\n getHexString: function () {\n\n return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 );\n\n },\n\n getHSL: function ( target ) {\n\n // h,s,l ranges are in 0.0 - 1.0\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Color: .getHSL() target is now required' );\n target = { h: 0, s: 0, l: 0 };\n\n }\n\n var r = this.r, g = this.g, b = this.b;\n\n var max = Math.max( r, g, b );\n var min = Math.min( r, g, b );\n\n var hue, saturation;\n var lightness = ( min + max ) / 2.0;\n\n if ( min === max ) {\n\n hue = 0;\n saturation = 0;\n\n } else {\n\n var delta = max - min;\n\n saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min );\n\n switch ( max ) {\n\n case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break;\n case g: hue = ( b - r ) / delta + 2; break;\n case b: hue = ( r - g ) / delta + 4; break;\n\n }\n\n hue /= 6;\n\n }\n\n target.h = hue;\n target.s = saturation;\n target.l = lightness;\n\n return target;\n\n },\n\n getStyle: function () {\n\n return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')';\n\n },\n\n offsetHSL: function () {\n\n var hsl = {};\n\n return function ( h, s, l ) {\n\n this.getHSL( hsl );\n\n hsl.h += h; hsl.s += s; hsl.l += l;\n\n this.setHSL( hsl.h, hsl.s, hsl.l );\n\n return this;\n\n };\n\n }(),\n\n add: function ( color ) {\n\n this.r += color.r;\n this.g += color.g;\n this.b += color.b;\n\n return this;\n\n },\n\n addColors: function ( color1, color2 ) {\n\n this.r = color1.r + color2.r;\n this.g = color1.g + color2.g;\n this.b = color1.b + color2.b;\n\n return this;\n\n },\n\n addScalar: function ( s ) {\n\n this.r += s;\n this.g += s;\n this.b += s;\n\n return this;\n\n },\n\n sub: function ( color ) {\n\n this.r = Math.max( 0, this.r - color.r );\n this.g = Math.max( 0, this.g - color.g );\n this.b = Math.max( 0, this.b - color.b );\n\n return this;\n\n },\n\n multiply: function ( color ) {\n\n this.r *= color.r;\n this.g *= color.g;\n this.b *= color.b;\n\n return this;\n\n },\n\n multiplyScalar: function ( s ) {\n\n this.r *= s;\n this.g *= s;\n this.b *= s;\n\n return this;\n\n },\n\n lerp: function ( color, alpha ) {\n\n this.r += ( color.r - this.r ) * alpha;\n this.g += ( color.g - this.g ) * alpha;\n this.b += ( color.b - this.b ) * alpha;\n\n return this;\n\n },\n\n equals: function ( c ) {\n\n return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b );\n\n },\n\n fromArray: function ( array, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.r = array[ offset ];\n this.g = array[ offset + 1 ];\n this.b = array[ offset + 2 ];\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this.r;\n array[ offset + 1 ] = this.g;\n array[ offset + 2 ] = this.b;\n\n return array;\n\n },\n\n toJSON: function () {\n\n return this.getHex();\n\n }\n\n } );\n\n /**\n * Uniforms library for shared webgl shaders\n */\n\n var UniformsLib = {\n\n common: {\n\n diffuse: { value: new Color( 0xeeeeee ) },\n opacity: { value: 1.0 },\n\n map: { value: null },\n uvTransform: { value: new Matrix3() },\n\n alphaMap: { value: null },\n\n },\n\n specularmap: {\n\n specularMap: { value: null },\n\n },\n\n envmap: {\n\n envMap: { value: null },\n flipEnvMap: { value: - 1 },\n reflectivity: { value: 1.0 },\n refractionRatio: { value: 0.98 },\n maxMipLevel: { value: 0 }\n\n },\n\n aomap: {\n\n aoMap: { value: null },\n aoMapIntensity: { value: 1 }\n\n },\n\n lightmap: {\n\n lightMap: { value: null },\n lightMapIntensity: { value: 1 }\n\n },\n\n emissivemap: {\n\n emissiveMap: { value: null }\n\n },\n\n bumpmap: {\n\n bumpMap: { value: null },\n bumpScale: { value: 1 }\n\n },\n\n normalmap: {\n\n normalMap: { value: null },\n normalScale: { value: new Vector2( 1, 1 ) }\n\n },\n\n displacementmap: {\n\n displacementMap: { value: null },\n displacementScale: { value: 1 },\n displacementBias: { value: 0 }\n\n },\n\n roughnessmap: {\n\n roughnessMap: { value: null }\n\n },\n\n metalnessmap: {\n\n metalnessMap: { value: null }\n\n },\n\n gradientmap: {\n\n gradientMap: { value: null }\n\n },\n\n fog: {\n\n fogDensity: { value: 0.00025 },\n fogNear: { value: 1 },\n fogFar: { value: 2000 },\n fogColor: { value: new Color( 0xffffff ) }\n\n },\n\n lights: {\n\n ambientLightColor: { value: [] },\n\n directionalLights: { value: [], properties: {\n direction: {},\n color: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {}\n } },\n\n directionalShadowMap: { value: [] },\n directionalShadowMatrix: { value: [] },\n\n spotLights: { value: [], properties: {\n color: {},\n position: {},\n direction: {},\n distance: {},\n coneCos: {},\n penumbraCos: {},\n decay: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {}\n } },\n\n spotShadowMap: { value: [] },\n spotShadowMatrix: { value: [] },\n\n pointLights: { value: [], properties: {\n color: {},\n position: {},\n decay: {},\n distance: {},\n\n shadow: {},\n shadowBias: {},\n shadowRadius: {},\n shadowMapSize: {},\n shadowCameraNear: {},\n shadowCameraFar: {}\n } },\n\n pointShadowMap: { value: [] },\n pointShadowMatrix: { value: [] },\n\n hemisphereLights: { value: [], properties: {\n direction: {},\n skyColor: {},\n groundColor: {}\n } },\n\n // TODO (abelnation): RectAreaLight BRDF data needs to be moved from example to main src\n rectAreaLights: { value: [], properties: {\n color: {},\n position: {},\n width: {},\n height: {}\n } }\n\n },\n\n points: {\n\n diffuse: { value: new Color( 0xeeeeee ) },\n opacity: { value: 1.0 },\n size: { value: 1.0 },\n scale: { value: 1.0 },\n map: { value: null },\n uvTransform: { value: new Matrix3() }\n\n }\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n */\n\n var ShaderLib = {\n\n basic: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.fog\n ] ),\n\n vertexShader: ShaderChunk.meshbasic_vert,\n fragmentShader: ShaderChunk.meshbasic_frag\n\n },\n\n lambert: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshlambert_vert,\n fragmentShader: ShaderChunk.meshlambert_frag\n\n },\n\n phong: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.specularmap,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n UniformsLib.gradientmap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) },\n specular: { value: new Color( 0x111111 ) },\n shininess: { value: 30 }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphong_vert,\n fragmentShader: ShaderChunk.meshphong_frag\n\n },\n\n standard: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.envmap,\n UniformsLib.aomap,\n UniformsLib.lightmap,\n UniformsLib.emissivemap,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n UniformsLib.roughnessmap,\n UniformsLib.metalnessmap,\n UniformsLib.fog,\n UniformsLib.lights,\n {\n emissive: { value: new Color( 0x000000 ) },\n roughness: { value: 0.5 },\n metalness: { value: 0.5 },\n envMapIntensity: { value: 1 } // temporary\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphysical_vert,\n fragmentShader: ShaderChunk.meshphysical_frag\n\n },\n\n points: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.points,\n UniformsLib.fog\n ] ),\n\n vertexShader: ShaderChunk.points_vert,\n fragmentShader: ShaderChunk.points_frag\n\n },\n\n dashed: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.fog,\n {\n scale: { value: 1 },\n dashSize: { value: 1 },\n totalSize: { value: 2 }\n }\n ] ),\n\n vertexShader: ShaderChunk.linedashed_vert,\n fragmentShader: ShaderChunk.linedashed_frag\n\n },\n\n depth: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.displacementmap\n ] ),\n\n vertexShader: ShaderChunk.depth_vert,\n fragmentShader: ShaderChunk.depth_frag\n\n },\n\n normal: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.bumpmap,\n UniformsLib.normalmap,\n UniformsLib.displacementmap,\n {\n opacity: { value: 1.0 }\n }\n ] ),\n\n vertexShader: ShaderChunk.normal_vert,\n fragmentShader: ShaderChunk.normal_frag\n\n },\n\n /* -------------------------------------------------------------------------\n // Cube map shader\n ------------------------------------------------------------------------- */\n\n cube: {\n\n uniforms: {\n tCube: { value: null },\n tFlip: { value: - 1 },\n opacity: { value: 1.0 }\n },\n\n vertexShader: ShaderChunk.cube_vert,\n fragmentShader: ShaderChunk.cube_frag\n\n },\n\n equirect: {\n\n uniforms: {\n tEquirect: { value: null },\n },\n\n vertexShader: ShaderChunk.equirect_vert,\n fragmentShader: ShaderChunk.equirect_frag\n\n },\n\n distanceRGBA: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.common,\n UniformsLib.displacementmap,\n {\n referencePosition: { value: new Vector3() },\n nearDistance: { value: 1 },\n farDistance: { value: 1000 }\n }\n ] ),\n\n vertexShader: ShaderChunk.distanceRGBA_vert,\n fragmentShader: ShaderChunk.distanceRGBA_frag\n\n },\n\n shadow: {\n\n uniforms: UniformsUtils.merge( [\n UniformsLib.lights,\n UniformsLib.fog,\n {\n color: { value: new Color( 0x00000 ) },\n opacity: { value: 1.0 }\n },\n ] ),\n\n vertexShader: ShaderChunk.shadow_vert,\n fragmentShader: ShaderChunk.shadow_frag\n\n }\n\n };\n\n ShaderLib.physical = {\n\n uniforms: UniformsUtils.merge( [\n ShaderLib.standard.uniforms,\n {\n clearCoat: { value: 0 },\n clearCoatRoughness: { value: 0 }\n }\n ] ),\n\n vertexShader: ShaderChunk.meshphysical_vert,\n fragmentShader: ShaderChunk.meshphysical_frag\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLAttributes( gl ) {\n\n var buffers = new WeakMap();\n\n function createBuffer( attribute, bufferType ) {\n\n var array = attribute.array;\n var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;\n\n var buffer = gl.createBuffer();\n\n gl.bindBuffer( bufferType, buffer );\n gl.bufferData( bufferType, array, usage );\n\n attribute.onUploadCallback();\n\n var type = gl.FLOAT;\n\n if ( array instanceof Float32Array ) {\n\n type = gl.FLOAT;\n\n } else if ( array instanceof Float64Array ) {\n\n console.warn( 'THREE.WebGLAttributes: Unsupported data buffer format: Float64Array.' );\n\n } else if ( array instanceof Uint16Array ) {\n\n type = gl.UNSIGNED_SHORT;\n\n } else if ( array instanceof Int16Array ) {\n\n type = gl.SHORT;\n\n } else if ( array instanceof Uint32Array ) {\n\n type = gl.UNSIGNED_INT;\n\n } else if ( array instanceof Int32Array ) {\n\n type = gl.INT;\n\n } else if ( array instanceof Int8Array ) {\n\n type = gl.BYTE;\n\n } else if ( array instanceof Uint8Array ) {\n\n type = gl.UNSIGNED_BYTE;\n\n }\n\n return {\n buffer: buffer,\n type: type,\n bytesPerElement: array.BYTES_PER_ELEMENT,\n version: attribute.version\n };\n\n }\n\n function updateBuffer( buffer, attribute, bufferType ) {\n\n var array = attribute.array;\n var updateRange = attribute.updateRange;\n\n gl.bindBuffer( bufferType, buffer );\n\n if ( attribute.dynamic === false ) {\n\n gl.bufferData( bufferType, array, gl.STATIC_DRAW );\n\n } else if ( updateRange.count === - 1 ) {\n\n // Not using update ranges\n\n gl.bufferSubData( bufferType, 0, array );\n\n } else if ( updateRange.count === 0 ) {\n\n console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );\n\n } else {\n\n gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,\n array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );\n\n updateRange.count = - 1; // reset range\n\n }\n\n }\n\n //\n\n function get( attribute ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n return buffers.get( attribute );\n\n }\n\n function remove( attribute ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n var data = buffers.get( attribute );\n\n if ( data ) {\n\n gl.deleteBuffer( data.buffer );\n\n buffers.delete( attribute );\n\n }\n\n }\n\n function update( attribute, bufferType ) {\n\n if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;\n\n var data = buffers.get( attribute );\n\n if ( data === undefined ) {\n\n buffers.set( attribute, createBuffer( attribute, bufferType ) );\n\n } else if ( data.version < attribute.version ) {\n\n updateBuffer( data.buffer, attribute, bufferType );\n\n data.version = attribute.version;\n\n }\n\n }\n\n return {\n\n get: get,\n remove: remove,\n update: update\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author bhouston / http://clara.io\n */\n\n function Euler( x, y, z, order ) {\n\n this._x = x || 0;\n this._y = y || 0;\n this._z = z || 0;\n this._order = order || Euler.DefaultOrder;\n\n }\n\n Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];\n\n Euler.DefaultOrder = 'XYZ';\n\n Object.defineProperties( Euler.prototype, {\n\n x: {\n\n get: function () {\n\n return this._x;\n\n },\n\n set: function ( value ) {\n\n this._x = value;\n this.onChangeCallback();\n\n }\n\n },\n\n y: {\n\n get: function () {\n\n return this._y;\n\n },\n\n set: function ( value ) {\n\n this._y = value;\n this.onChangeCallback();\n\n }\n\n },\n\n z: {\n\n get: function () {\n\n return this._z;\n\n },\n\n set: function ( value ) {\n\n this._z = value;\n this.onChangeCallback();\n\n }\n\n },\n\n order: {\n\n get: function () {\n\n return this._order;\n\n },\n\n set: function ( value ) {\n\n this._order = value;\n this.onChangeCallback();\n\n }\n\n }\n\n } );\n\n Object.assign( Euler.prototype, {\n\n isEuler: true,\n\n set: function ( x, y, z, order ) {\n\n this._x = x;\n this._y = y;\n this._z = z;\n this._order = order || this._order;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this._x, this._y, this._z, this._order );\n\n },\n\n copy: function ( euler ) {\n\n this._x = euler._x;\n this._y = euler._y;\n this._z = euler._z;\n this._order = euler._order;\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n setFromRotationMatrix: function ( m, order, update ) {\n\n var clamp = _Math.clamp;\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n var te = m.elements;\n var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];\n var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];\n var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];\n\n order = order || this._order;\n\n if ( order === 'XYZ' ) {\n\n this._y = Math.asin( clamp( m13, - 1, 1 ) );\n\n if ( Math.abs( m13 ) < 0.99999 ) {\n\n this._x = Math.atan2( - m23, m33 );\n this._z = Math.atan2( - m12, m11 );\n\n } else {\n\n this._x = Math.atan2( m32, m22 );\n this._z = 0;\n\n }\n\n } else if ( order === 'YXZ' ) {\n\n this._x = Math.asin( - clamp( m23, - 1, 1 ) );\n\n if ( Math.abs( m23 ) < 0.99999 ) {\n\n this._y = Math.atan2( m13, m33 );\n this._z = Math.atan2( m21, m22 );\n\n } else {\n\n this._y = Math.atan2( - m31, m11 );\n this._z = 0;\n\n }\n\n } else if ( order === 'ZXY' ) {\n\n this._x = Math.asin( clamp( m32, - 1, 1 ) );\n\n if ( Math.abs( m32 ) < 0.99999 ) {\n\n this._y = Math.atan2( - m31, m33 );\n this._z = Math.atan2( - m12, m22 );\n\n } else {\n\n this._y = 0;\n this._z = Math.atan2( m21, m11 );\n\n }\n\n } else if ( order === 'ZYX' ) {\n\n this._y = Math.asin( - clamp( m31, - 1, 1 ) );\n\n if ( Math.abs( m31 ) < 0.99999 ) {\n\n this._x = Math.atan2( m32, m33 );\n this._z = Math.atan2( m21, m11 );\n\n } else {\n\n this._x = 0;\n this._z = Math.atan2( - m12, m22 );\n\n }\n\n } else if ( order === 'YZX' ) {\n\n this._z = Math.asin( clamp( m21, - 1, 1 ) );\n\n if ( Math.abs( m21 ) < 0.99999 ) {\n\n this._x = Math.atan2( - m23, m22 );\n this._y = Math.atan2( - m31, m11 );\n\n } else {\n\n this._x = 0;\n this._y = Math.atan2( m13, m33 );\n\n }\n\n } else if ( order === 'XZY' ) {\n\n this._z = Math.asin( - clamp( m12, - 1, 1 ) );\n\n if ( Math.abs( m12 ) < 0.99999 ) {\n\n this._x = Math.atan2( m32, m22 );\n this._y = Math.atan2( m13, m11 );\n\n } else {\n\n this._x = Math.atan2( - m23, m33 );\n this._y = 0;\n\n }\n\n } else {\n\n console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order );\n\n }\n\n this._order = order;\n\n if ( update !== false ) this.onChangeCallback();\n\n return this;\n\n },\n\n setFromQuaternion: function () {\n\n var matrix = new Matrix4();\n\n return function setFromQuaternion( q, order, update ) {\n\n matrix.makeRotationFromQuaternion( q );\n\n return this.setFromRotationMatrix( matrix, order, update );\n\n };\n\n }(),\n\n setFromVector3: function ( v, order ) {\n\n return this.set( v.x, v.y, v.z, order || this._order );\n\n },\n\n reorder: function () {\n\n // WARNING: this discards revolution information -bhouston\n\n var q = new Quaternion();\n\n return function reorder( newOrder ) {\n\n q.setFromEuler( this );\n\n return this.setFromQuaternion( q, newOrder );\n\n };\n\n }(),\n\n equals: function ( euler ) {\n\n return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );\n\n },\n\n fromArray: function ( array ) {\n\n this._x = array[ 0 ];\n this._y = array[ 1 ];\n this._z = array[ 2 ];\n if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];\n\n this.onChangeCallback();\n\n return this;\n\n },\n\n toArray: function ( array, offset ) {\n\n if ( array === undefined ) array = [];\n if ( offset === undefined ) offset = 0;\n\n array[ offset ] = this._x;\n array[ offset + 1 ] = this._y;\n array[ offset + 2 ] = this._z;\n array[ offset + 3 ] = this._order;\n\n return array;\n\n },\n\n toVector3: function ( optionalResult ) {\n\n if ( optionalResult ) {\n\n return optionalResult.set( this._x, this._y, this._z );\n\n } else {\n\n return new Vector3( this._x, this._y, this._z );\n\n }\n\n },\n\n onChange: function ( callback ) {\n\n this.onChangeCallback = callback;\n\n return this;\n\n },\n\n onChangeCallback: function () {}\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Layers() {\n\n this.mask = 1 | 0;\n\n }\n\n Object.assign( Layers.prototype, {\n\n set: function ( channel ) {\n\n this.mask = 1 << channel | 0;\n\n },\n\n enable: function ( channel ) {\n\n this.mask |= 1 << channel | 0;\n\n },\n\n toggle: function ( channel ) {\n\n this.mask ^= 1 << channel | 0;\n\n },\n\n disable: function ( channel ) {\n\n this.mask &= ~ ( 1 << channel | 0 );\n\n },\n\n test: function ( layers ) {\n\n return ( this.mask & layers.mask ) !== 0;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author WestLangley / http://github.com/WestLangley\n * @author elephantatwork / www.elephantatwork.ch\n */\n\n var object3DId = 0;\n\n function Object3D() {\n\n Object.defineProperty( this, 'id', { value: object3DId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Object3D';\n\n this.parent = null;\n this.children = [];\n\n this.up = Object3D.DefaultUp.clone();\n\n var position = new Vector3();\n var rotation = new Euler();\n var quaternion = new Quaternion();\n var scale = new Vector3( 1, 1, 1 );\n\n function onRotationChange() {\n\n quaternion.setFromEuler( rotation, false );\n\n }\n\n function onQuaternionChange() {\n\n rotation.setFromQuaternion( quaternion, undefined, false );\n\n }\n\n rotation.onChange( onRotationChange );\n quaternion.onChange( onQuaternionChange );\n\n Object.defineProperties( this, {\n position: {\n enumerable: true,\n value: position\n },\n rotation: {\n enumerable: true,\n value: rotation\n },\n quaternion: {\n enumerable: true,\n value: quaternion\n },\n scale: {\n enumerable: true,\n value: scale\n },\n modelViewMatrix: {\n value: new Matrix4()\n },\n normalMatrix: {\n value: new Matrix3()\n }\n } );\n\n this.matrix = new Matrix4();\n this.matrixWorld = new Matrix4();\n\n this.matrixAutoUpdate = Object3D.DefaultMatrixAutoUpdate;\n this.matrixWorldNeedsUpdate = false;\n\n this.layers = new Layers();\n this.visible = true;\n\n this.castShadow = false;\n this.receiveShadow = false;\n\n this.frustumCulled = true;\n this.renderOrder = 0;\n\n this.userData = {};\n\n }\n\n Object3D.DefaultUp = new Vector3( 0, 1, 0 );\n Object3D.DefaultMatrixAutoUpdate = true;\n\n Object3D.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Object3D,\n\n isObject3D: true,\n\n onBeforeRender: function () {},\n onAfterRender: function () {},\n\n applyMatrix: function ( matrix ) {\n\n this.matrix.multiplyMatrices( matrix, this.matrix );\n\n this.matrix.decompose( this.position, this.quaternion, this.scale );\n\n },\n\n applyQuaternion: function ( q ) {\n\n this.quaternion.premultiply( q );\n\n return this;\n\n },\n\n setRotationFromAxisAngle: function ( axis, angle ) {\n\n // assumes axis is normalized\n\n this.quaternion.setFromAxisAngle( axis, angle );\n\n },\n\n setRotationFromEuler: function ( euler ) {\n\n this.quaternion.setFromEuler( euler, true );\n\n },\n\n setRotationFromMatrix: function ( m ) {\n\n // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)\n\n this.quaternion.setFromRotationMatrix( m );\n\n },\n\n setRotationFromQuaternion: function ( q ) {\n\n // assumes q is normalized\n\n this.quaternion.copy( q );\n\n },\n\n rotateOnAxis: function () {\n\n // rotate object on axis in object space\n // axis is assumed to be normalized\n\n var q1 = new Quaternion();\n\n return function rotateOnAxis( axis, angle ) {\n\n q1.setFromAxisAngle( axis, angle );\n\n this.quaternion.multiply( q1 );\n\n return this;\n\n };\n\n }(),\n\n rotateOnWorldAxis: function () {\n\n // rotate object on axis in world space\n // axis is assumed to be normalized\n // method assumes no rotated parent\n\n var q1 = new Quaternion();\n\n return function rotateOnWorldAxis( axis, angle ) {\n\n q1.setFromAxisAngle( axis, angle );\n\n this.quaternion.premultiply( q1 );\n\n return this;\n\n };\n\n }(),\n\n rotateX: function () {\n\n var v1 = new Vector3( 1, 0, 0 );\n\n return function rotateX( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n rotateY: function () {\n\n var v1 = new Vector3( 0, 1, 0 );\n\n return function rotateY( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n rotateZ: function () {\n\n var v1 = new Vector3( 0, 0, 1 );\n\n return function rotateZ( angle ) {\n\n return this.rotateOnAxis( v1, angle );\n\n };\n\n }(),\n\n translateOnAxis: function () {\n\n // translate object by distance along axis in object space\n // axis is assumed to be normalized\n\n var v1 = new Vector3();\n\n return function translateOnAxis( axis, distance ) {\n\n v1.copy( axis ).applyQuaternion( this.quaternion );\n\n this.position.add( v1.multiplyScalar( distance ) );\n\n return this;\n\n };\n\n }(),\n\n translateX: function () {\n\n var v1 = new Vector3( 1, 0, 0 );\n\n return function translateX( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n translateY: function () {\n\n var v1 = new Vector3( 0, 1, 0 );\n\n return function translateY( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n translateZ: function () {\n\n var v1 = new Vector3( 0, 0, 1 );\n\n return function translateZ( distance ) {\n\n return this.translateOnAxis( v1, distance );\n\n };\n\n }(),\n\n localToWorld: function ( vector ) {\n\n return vector.applyMatrix4( this.matrixWorld );\n\n },\n\n worldToLocal: function () {\n\n var m1 = new Matrix4();\n\n return function worldToLocal( vector ) {\n\n return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) );\n\n };\n\n }(),\n\n lookAt: function () {\n\n // This method does not support objects with rotated and/or translated parent(s)\n\n var m1 = new Matrix4();\n var vector = new Vector3();\n\n return function lookAt( x, y, z ) {\n\n if ( x.isVector3 ) {\n\n vector.copy( x );\n\n } else {\n\n vector.set( x, y, z );\n\n }\n\n if ( this.isCamera ) {\n\n m1.lookAt( this.position, vector, this.up );\n\n } else {\n\n m1.lookAt( vector, this.position, this.up );\n\n }\n\n this.quaternion.setFromRotationMatrix( m1 );\n\n };\n\n }(),\n\n add: function ( object ) {\n\n if ( arguments.length > 1 ) {\n\n for ( var i = 0; i < arguments.length; i ++ ) {\n\n this.add( arguments[ i ] );\n\n }\n\n return this;\n\n }\n\n if ( object === this ) {\n\n console.error( \"THREE.Object3D.add: object can't be added as a child of itself.\", object );\n return this;\n\n }\n\n if ( ( object && object.isObject3D ) ) {\n\n if ( object.parent !== null ) {\n\n object.parent.remove( object );\n\n }\n\n object.parent = this;\n object.dispatchEvent( { type: 'added' } );\n\n this.children.push( object );\n\n } else {\n\n console.error( \"THREE.Object3D.add: object not an instance of THREE.Object3D.\", object );\n\n }\n\n return this;\n\n },\n\n remove: function ( object ) {\n\n if ( arguments.length > 1 ) {\n\n for ( var i = 0; i < arguments.length; i ++ ) {\n\n this.remove( arguments[ i ] );\n\n }\n\n return this;\n\n }\n\n var index = this.children.indexOf( object );\n\n if ( index !== - 1 ) {\n\n object.parent = null;\n\n object.dispatchEvent( { type: 'removed' } );\n\n this.children.splice( index, 1 );\n\n }\n\n return this;\n\n },\n\n getObjectById: function ( id ) {\n\n return this.getObjectByProperty( 'id', id );\n\n },\n\n getObjectByName: function ( name ) {\n\n return this.getObjectByProperty( 'name', name );\n\n },\n\n getObjectByProperty: function ( name, value ) {\n\n if ( this[ name ] === value ) return this;\n\n for ( var i = 0, l = this.children.length; i < l; i ++ ) {\n\n var child = this.children[ i ];\n var object = child.getObjectByProperty( name, value );\n\n if ( object !== undefined ) {\n\n return object;\n\n }\n\n }\n\n return undefined;\n\n },\n\n getWorldPosition: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldPosition() target is now required' );\n target = new Vector3();\n\n }\n\n this.updateMatrixWorld( true );\n\n return target.setFromMatrixPosition( this.matrixWorld );\n\n },\n\n getWorldQuaternion: function () {\n\n var position = new Vector3();\n var scale = new Vector3();\n\n return function getWorldQuaternion( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldQuaternion() target is now required' );\n target = new Quaternion();\n\n }\n\n this.updateMatrixWorld( true );\n\n this.matrixWorld.decompose( position, target, scale );\n\n return target;\n\n };\n\n }(),\n\n getWorldScale: function () {\n\n var position = new Vector3();\n var quaternion = new Quaternion();\n\n return function getWorldScale( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldScale() target is now required' );\n target = new Vector3();\n\n }\n\n this.updateMatrixWorld( true );\n\n this.matrixWorld.decompose( position, quaternion, target );\n\n return target;\n\n };\n\n }(),\n\n getWorldDirection: function () {\n\n var quaternion = new Quaternion();\n\n return function getWorldDirection( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Object3D: .getWorldDirection() target is now required' );\n target = new Vector3();\n\n }\n\n this.getWorldQuaternion( quaternion );\n\n return target.set( 0, 0, 1 ).applyQuaternion( quaternion );\n\n };\n\n }(),\n\n raycast: function () {},\n\n traverse: function ( callback ) {\n\n callback( this );\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].traverse( callback );\n\n }\n\n },\n\n traverseVisible: function ( callback ) {\n\n if ( this.visible === false ) return;\n\n callback( this );\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].traverseVisible( callback );\n\n }\n\n },\n\n traverseAncestors: function ( callback ) {\n\n var parent = this.parent;\n\n if ( parent !== null ) {\n\n callback( parent );\n\n parent.traverseAncestors( callback );\n\n }\n\n },\n\n updateMatrix: function () {\n\n this.matrix.compose( this.position, this.quaternion, this.scale );\n\n this.matrixWorldNeedsUpdate = true;\n\n },\n\n updateMatrixWorld: function ( force ) {\n\n if ( this.matrixAutoUpdate ) this.updateMatrix();\n\n if ( this.matrixWorldNeedsUpdate || force ) {\n\n if ( this.parent === null ) {\n\n this.matrixWorld.copy( this.matrix );\n\n } else {\n\n this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );\n\n }\n\n this.matrixWorldNeedsUpdate = false;\n\n force = true;\n\n }\n\n // update children\n\n var children = this.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n children[ i ].updateMatrixWorld( force );\n\n }\n\n },\n\n toJSON: function ( meta ) {\n\n // meta is a string when called from JSON.stringify\n var isRootObject = ( meta === undefined || typeof meta === 'string' );\n\n var output = {};\n\n // meta is a hash used to collect geometries, materials.\n // not providing it implies that this is the root object\n // being serialized.\n if ( isRootObject ) {\n\n // initialize meta obj\n meta = {\n geometries: {},\n materials: {},\n textures: {},\n images: {},\n shapes: {}\n };\n\n output.metadata = {\n version: 4.5,\n type: 'Object',\n generator: 'Object3D.toJSON'\n };\n\n }\n\n // standard Object3D serialization\n\n var object = {};\n\n object.uuid = this.uuid;\n object.type = this.type;\n\n if ( this.name !== '' ) object.name = this.name;\n if ( this.castShadow === true ) object.castShadow = true;\n if ( this.receiveShadow === true ) object.receiveShadow = true;\n if ( this.visible === false ) object.visible = false;\n if ( this.frustumCulled === false ) object.frustumCulled = false;\n if ( this.renderOrder !== 0 ) object.renderOrder = this.renderOrder;\n if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData;\n\n object.matrix = this.matrix.toArray();\n\n //\n\n function serialize( library, element ) {\n\n if ( library[ element.uuid ] === undefined ) {\n\n library[ element.uuid ] = element.toJSON( meta );\n\n }\n\n return element.uuid;\n\n }\n\n if ( this.geometry !== undefined ) {\n\n object.geometry = serialize( meta.geometries, this.geometry );\n\n var parameters = this.geometry.parameters;\n\n if ( parameters !== undefined && parameters.shapes !== undefined ) {\n\n var shapes = parameters.shapes;\n\n if ( Array.isArray( shapes ) ) {\n\n for ( var i = 0, l = shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ i ];\n\n serialize( meta.shapes, shape );\n\n }\n\n } else {\n\n serialize( meta.shapes, shapes );\n\n }\n\n }\n\n }\n\n if ( this.material !== undefined ) {\n\n if ( Array.isArray( this.material ) ) {\n\n var uuids = [];\n\n for ( var i = 0, l = this.material.length; i < l; i ++ ) {\n\n uuids.push( serialize( meta.materials, this.material[ i ] ) );\n\n }\n\n object.material = uuids;\n\n } else {\n\n object.material = serialize( meta.materials, this.material );\n\n }\n\n }\n\n //\n\n if ( this.children.length > 0 ) {\n\n object.children = [];\n\n for ( var i = 0; i < this.children.length; i ++ ) {\n\n object.children.push( this.children[ i ].toJSON( meta ).object );\n\n }\n\n }\n\n if ( isRootObject ) {\n\n var geometries = extractFromCache( meta.geometries );\n var materials = extractFromCache( meta.materials );\n var textures = extractFromCache( meta.textures );\n var images = extractFromCache( meta.images );\n var shapes = extractFromCache( meta.shapes );\n\n if ( geometries.length > 0 ) output.geometries = geometries;\n if ( materials.length > 0 ) output.materials = materials;\n if ( textures.length > 0 ) output.textures = textures;\n if ( images.length > 0 ) output.images = images;\n if ( shapes.length > 0 ) output.shapes = shapes;\n\n }\n\n output.object = object;\n\n return output;\n\n // extract data from the cache hash\n // remove metadata on each item\n // and return as array\n function extractFromCache( cache ) {\n\n var values = [];\n for ( var key in cache ) {\n\n var data = cache[ key ];\n delete data.metadata;\n values.push( data );\n\n }\n return values;\n\n }\n\n },\n\n clone: function ( recursive ) {\n\n return new this.constructor().copy( this, recursive );\n\n },\n\n copy: function ( source, recursive ) {\n\n if ( recursive === undefined ) recursive = true;\n\n this.name = source.name;\n\n this.up.copy( source.up );\n\n this.position.copy( source.position );\n this.quaternion.copy( source.quaternion );\n this.scale.copy( source.scale );\n\n this.matrix.copy( source.matrix );\n this.matrixWorld.copy( source.matrixWorld );\n\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate;\n\n this.layers.mask = source.layers.mask;\n this.visible = source.visible;\n\n this.castShadow = source.castShadow;\n this.receiveShadow = source.receiveShadow;\n\n this.frustumCulled = source.frustumCulled;\n this.renderOrder = source.renderOrder;\n\n this.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n if ( recursive === true ) {\n\n for ( var i = 0; i < source.children.length; i ++ ) {\n\n var child = source.children[ i ];\n this.add( child.clone() );\n\n }\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author mikael emtinger / http://gomo.se/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Camera() {\n\n Object3D.call( this );\n\n this.type = 'Camera';\n\n this.matrixWorldInverse = new Matrix4();\n this.projectionMatrix = new Matrix4();\n\n }\n\n Camera.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Camera,\n\n isCamera: true,\n\n copy: function ( source, recursive ) {\n\n Object3D.prototype.copy.call( this, source, recursive );\n\n this.matrixWorldInverse.copy( source.matrixWorldInverse );\n this.projectionMatrix.copy( source.projectionMatrix );\n\n return this;\n\n },\n\n getWorldDirection: function () {\n\n var quaternion = new Quaternion();\n\n return function getWorldDirection( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Camera: .getWorldDirection() target is now required' );\n target = new Vector3();\n\n }\n\n this.getWorldQuaternion( quaternion );\n\n return target.set( 0, 0, - 1 ).applyQuaternion( quaternion );\n\n };\n\n }(),\n\n updateMatrixWorld: function ( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n this.matrixWorldInverse.getInverse( this.matrixWorld );\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author arose / http://github.com/arose\n */\n\n function OrthographicCamera( left, right, top, bottom, near, far ) {\n\n Camera.call( this );\n\n this.type = 'OrthographicCamera';\n\n this.zoom = 1;\n this.view = null;\n\n this.left = left;\n this.right = right;\n this.top = top;\n this.bottom = bottom;\n\n this.near = ( near !== undefined ) ? near : 0.1;\n this.far = ( far !== undefined ) ? far : 2000;\n\n this.updateProjectionMatrix();\n\n }\n\n OrthographicCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n constructor: OrthographicCamera,\n\n isOrthographicCamera: true,\n\n copy: function ( source, recursive ) {\n\n Camera.prototype.copy.call( this, source, recursive );\n\n this.left = source.left;\n this.right = source.right;\n this.top = source.top;\n this.bottom = source.bottom;\n this.near = source.near;\n this.far = source.far;\n\n this.zoom = source.zoom;\n this.view = source.view === null ? null : Object.assign( {}, source.view );\n\n return this;\n\n },\n\n setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {\n\n if ( this.view === null ) {\n\n this.view = {\n enabled: true,\n fullWidth: 1,\n fullHeight: 1,\n offsetX: 0,\n offsetY: 0,\n width: 1,\n height: 1\n };\n\n }\n\n this.view.enabled = true;\n this.view.fullWidth = fullWidth;\n this.view.fullHeight = fullHeight;\n this.view.offsetX = x;\n this.view.offsetY = y;\n this.view.width = width;\n this.view.height = height;\n\n this.updateProjectionMatrix();\n\n },\n\n clearViewOffset: function () {\n\n if ( this.view !== null ) {\n\n this.view.enabled = false;\n\n }\n\n this.updateProjectionMatrix();\n\n },\n\n updateProjectionMatrix: function () {\n\n var dx = ( this.right - this.left ) / ( 2 * this.zoom );\n var dy = ( this.top - this.bottom ) / ( 2 * this.zoom );\n var cx = ( this.right + this.left ) / 2;\n var cy = ( this.top + this.bottom ) / 2;\n\n var left = cx - dx;\n var right = cx + dx;\n var top = cy + dy;\n var bottom = cy - dy;\n\n if ( this.view !== null && this.view.enabled ) {\n\n var zoomW = this.zoom / ( this.view.width / this.view.fullWidth );\n var zoomH = this.zoom / ( this.view.height / this.view.fullHeight );\n var scaleW = ( this.right - this.left ) / this.view.width;\n var scaleH = ( this.top - this.bottom ) / this.view.height;\n\n left += scaleW * ( this.view.offsetX / zoomW );\n right = left + scaleW * ( this.view.width / zoomW );\n top -= scaleH * ( this.view.offsetY / zoomH );\n bottom = top - scaleH * ( this.view.height / zoomH );\n\n }\n\n this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far );\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.zoom = this.zoom;\n data.object.left = this.left;\n data.object.right = this.right;\n data.object.top = this.top;\n data.object.bottom = this.bottom;\n data.object.near = this.near;\n data.object.far = this.far;\n\n if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Face3( a, b, c, normal, color, materialIndex ) {\n\n this.a = a;\n this.b = b;\n this.c = c;\n\n this.normal = ( normal && normal.isVector3 ) ? normal : new Vector3();\n this.vertexNormals = Array.isArray( normal ) ? normal : [];\n\n this.color = ( color && color.isColor ) ? color : new Color();\n this.vertexColors = Array.isArray( color ) ? color : [];\n\n this.materialIndex = materialIndex !== undefined ? materialIndex : 0;\n\n }\n\n Object.assign( Face3.prototype, {\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.a = source.a;\n this.b = source.b;\n this.c = source.c;\n\n this.normal.copy( source.normal );\n this.color.copy( source.color );\n\n this.materialIndex = source.materialIndex;\n\n for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) {\n\n this.vertexNormals[ i ] = source.vertexNormals[ i ].clone();\n\n }\n\n for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) {\n\n this.vertexColors[ i ] = source.vertexColors[ i ].clone();\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author kile / http://kile.stravaganza.org/\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author bhouston / http://clara.io\n */\n\n var geometryId = 0; // Geometry uses even numbers as Id\n\n function Geometry() {\n\n Object.defineProperty( this, 'id', { value: geometryId += 2 } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Geometry';\n\n this.vertices = [];\n this.colors = [];\n this.faces = [];\n this.faceVertexUvs = [[]];\n\n this.morphTargets = [];\n this.morphNormals = [];\n\n this.skinWeights = [];\n this.skinIndices = [];\n\n this.lineDistances = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // update flags\n\n this.elementsNeedUpdate = false;\n this.verticesNeedUpdate = false;\n this.uvsNeedUpdate = false;\n this.normalsNeedUpdate = false;\n this.colorsNeedUpdate = false;\n this.lineDistancesNeedUpdate = false;\n this.groupsNeedUpdate = false;\n\n }\n\n Geometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Geometry,\n\n isGeometry: true,\n\n applyMatrix: function ( matrix ) {\n\n var normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n for ( var i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n var vertex = this.vertices[ i ];\n vertex.applyMatrix4( matrix );\n\n }\n\n for ( var i = 0, il = this.faces.length; i < il; i ++ ) {\n\n var face = this.faces[ i ];\n face.normal.applyMatrix3( normalMatrix ).normalize();\n\n for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {\n\n face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize();\n\n }\n\n }\n\n if ( this.boundingBox !== null ) {\n\n this.computeBoundingBox();\n\n }\n\n if ( this.boundingSphere !== null ) {\n\n this.computeBoundingSphere();\n\n }\n\n this.verticesNeedUpdate = true;\n this.normalsNeedUpdate = true;\n\n return this;\n\n },\n\n rotateX: function () {\n\n // rotate geometry around world x-axis\n\n var m1 = new Matrix4();\n\n return function rotateX( angle ) {\n\n m1.makeRotationX( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateY: function () {\n\n // rotate geometry around world y-axis\n\n var m1 = new Matrix4();\n\n return function rotateY( angle ) {\n\n m1.makeRotationY( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateZ: function () {\n\n // rotate geometry around world z-axis\n\n var m1 = new Matrix4();\n\n return function rotateZ( angle ) {\n\n m1.makeRotationZ( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n translate: function () {\n\n // translate geometry\n\n var m1 = new Matrix4();\n\n return function translate( x, y, z ) {\n\n m1.makeTranslation( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n scale: function () {\n\n // scale geometry\n\n var m1 = new Matrix4();\n\n return function scale( x, y, z ) {\n\n m1.makeScale( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n lookAt: function () {\n\n var obj = new Object3D();\n\n return function lookAt( vector ) {\n\n obj.lookAt( vector );\n\n obj.updateMatrix();\n\n this.applyMatrix( obj.matrix );\n\n };\n\n }(),\n\n fromBufferGeometry: function ( geometry ) {\n\n var scope = this;\n\n var indices = geometry.index !== null ? geometry.index.array : undefined;\n var attributes = geometry.attributes;\n\n var positions = attributes.position.array;\n var normals = attributes.normal !== undefined ? attributes.normal.array : undefined;\n var colors = attributes.color !== undefined ? attributes.color.array : undefined;\n var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined;\n var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined;\n\n if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = [];\n\n var tempNormals = [];\n var tempUVs = [];\n var tempUVs2 = [];\n\n for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) {\n\n scope.vertices.push( new Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) );\n\n if ( normals !== undefined ) {\n\n tempNormals.push( new Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) );\n\n }\n\n if ( colors !== undefined ) {\n\n scope.colors.push( new Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) );\n\n }\n\n if ( uvs !== undefined ) {\n\n tempUVs.push( new Vector2( uvs[ j ], uvs[ j + 1 ] ) );\n\n }\n\n if ( uvs2 !== undefined ) {\n\n tempUVs2.push( new Vector2( uvs2[ j ], uvs2[ j + 1 ] ) );\n\n }\n\n }\n\n function addFace( a, b, c, materialIndex ) {\n\n var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : [];\n var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : [];\n\n var face = new Face3( a, b, c, vertexNormals, vertexColors, materialIndex );\n\n scope.faces.push( face );\n\n if ( uvs !== undefined ) {\n\n scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] );\n\n }\n\n if ( uvs2 !== undefined ) {\n\n scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] );\n\n }\n\n }\n\n var groups = geometry.groups;\n\n if ( groups.length > 0 ) {\n\n for ( var i = 0; i < groups.length; i ++ ) {\n\n var group = groups[ i ];\n\n var start = group.start;\n var count = group.count;\n\n for ( var j = start, jl = start + count; j < jl; j += 3 ) {\n\n if ( indices !== undefined ) {\n\n addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ], group.materialIndex );\n\n } else {\n\n addFace( j, j + 1, j + 2, group.materialIndex );\n\n }\n\n }\n\n }\n\n } else {\n\n if ( indices !== undefined ) {\n\n for ( var i = 0; i < indices.length; i += 3 ) {\n\n addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] );\n\n }\n\n } else {\n\n for ( var i = 0; i < positions.length / 3; i += 3 ) {\n\n addFace( i, i + 1, i + 2 );\n\n }\n\n }\n\n }\n\n this.computeFaceNormals();\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n return this;\n\n },\n\n center: function () {\n\n var offset = new Vector3();\n\n return function center() {\n\n this.computeBoundingBox();\n\n this.boundingBox.getCenter( offset ).negate();\n\n this.translate( offset.x, offset.y, offset.z );\n\n return this;\n\n };\n\n }(),\n\n normalize: function () {\n\n this.computeBoundingSphere();\n\n var center = this.boundingSphere.center;\n var radius = this.boundingSphere.radius;\n\n var s = radius === 0 ? 1 : 1.0 / radius;\n\n var matrix = new Matrix4();\n matrix.set(\n s, 0, 0, - s * center.x,\n 0, s, 0, - s * center.y,\n 0, 0, s, - s * center.z,\n 0, 0, 0, 1\n );\n\n this.applyMatrix( matrix );\n\n return this;\n\n },\n\n computeFaceNormals: function () {\n\n var cb = new Vector3(), ab = new Vector3();\n\n for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n var face = this.faces[ f ];\n\n var vA = this.vertices[ face.a ];\n var vB = this.vertices[ face.b ];\n var vC = this.vertices[ face.c ];\n\n cb.subVectors( vC, vB );\n ab.subVectors( vA, vB );\n cb.cross( ab );\n\n cb.normalize();\n\n face.normal.copy( cb );\n\n }\n\n },\n\n computeVertexNormals: function ( areaWeighted ) {\n\n if ( areaWeighted === undefined ) areaWeighted = true;\n\n var v, vl, f, fl, face, vertices;\n\n vertices = new Array( this.vertices.length );\n\n for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n vertices[ v ] = new Vector3();\n\n }\n\n if ( areaWeighted ) {\n\n // vertex normals weighted by triangle areas\n // http://www.iquilezles.org/www/articles/normals/normals.htm\n\n var vA, vB, vC;\n var cb = new Vector3(), ab = new Vector3();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n vA = this.vertices[ face.a ];\n vB = this.vertices[ face.b ];\n vC = this.vertices[ face.c ];\n\n cb.subVectors( vC, vB );\n ab.subVectors( vA, vB );\n cb.cross( ab );\n\n vertices[ face.a ].add( cb );\n vertices[ face.b ].add( cb );\n vertices[ face.c ].add( cb );\n\n }\n\n } else {\n\n this.computeFaceNormals();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n vertices[ face.a ].add( face.normal );\n vertices[ face.b ].add( face.normal );\n vertices[ face.c ].add( face.normal );\n\n }\n\n }\n\n for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) {\n\n vertices[ v ].normalize();\n\n }\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n vertexNormals[ 0 ].copy( vertices[ face.a ] );\n vertexNormals[ 1 ].copy( vertices[ face.b ] );\n vertexNormals[ 2 ].copy( vertices[ face.c ] );\n\n } else {\n\n vertexNormals[ 0 ] = vertices[ face.a ].clone();\n vertexNormals[ 1 ] = vertices[ face.b ].clone();\n vertexNormals[ 2 ] = vertices[ face.c ].clone();\n\n }\n\n }\n\n if ( this.faces.length > 0 ) {\n\n this.normalsNeedUpdate = true;\n\n }\n\n },\n\n computeFlatVertexNormals: function () {\n\n var f, fl, face;\n\n this.computeFaceNormals();\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n vertexNormals[ 0 ].copy( face.normal );\n vertexNormals[ 1 ].copy( face.normal );\n vertexNormals[ 2 ].copy( face.normal );\n\n } else {\n\n vertexNormals[ 0 ] = face.normal.clone();\n vertexNormals[ 1 ] = face.normal.clone();\n vertexNormals[ 2 ] = face.normal.clone();\n\n }\n\n }\n\n if ( this.faces.length > 0 ) {\n\n this.normalsNeedUpdate = true;\n\n }\n\n },\n\n computeMorphNormals: function () {\n\n var i, il, f, fl, face;\n\n // save original normals\n // - create temp variables on first access\n // otherwise just copy (for faster repeated calls)\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n if ( ! face.__originalFaceNormal ) {\n\n face.__originalFaceNormal = face.normal.clone();\n\n } else {\n\n face.__originalFaceNormal.copy( face.normal );\n\n }\n\n if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = [];\n\n for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) {\n\n if ( ! face.__originalVertexNormals[ i ] ) {\n\n face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone();\n\n } else {\n\n face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] );\n\n }\n\n }\n\n }\n\n // use temp geometry to compute face and vertex normals for each morph\n\n var tmpGeo = new Geometry();\n tmpGeo.faces = this.faces;\n\n for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) {\n\n // create on first access\n\n if ( ! this.morphNormals[ i ] ) {\n\n this.morphNormals[ i ] = {};\n this.morphNormals[ i ].faceNormals = [];\n this.morphNormals[ i ].vertexNormals = [];\n\n var dstNormalsFace = this.morphNormals[ i ].faceNormals;\n var dstNormalsVertex = this.morphNormals[ i ].vertexNormals;\n\n var faceNormal, vertexNormals;\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n faceNormal = new Vector3();\n vertexNormals = { a: new Vector3(), b: new Vector3(), c: new Vector3() };\n\n dstNormalsFace.push( faceNormal );\n dstNormalsVertex.push( vertexNormals );\n\n }\n\n }\n\n var morphNormals = this.morphNormals[ i ];\n\n // set vertices to morph target\n\n tmpGeo.vertices = this.morphTargets[ i ].vertices;\n\n // compute morph normals\n\n tmpGeo.computeFaceNormals();\n tmpGeo.computeVertexNormals();\n\n // store morph normals\n\n var faceNormal, vertexNormals;\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n faceNormal = morphNormals.faceNormals[ f ];\n vertexNormals = morphNormals.vertexNormals[ f ];\n\n faceNormal.copy( face.normal );\n\n vertexNormals.a.copy( face.vertexNormals[ 0 ] );\n vertexNormals.b.copy( face.vertexNormals[ 1 ] );\n vertexNormals.c.copy( face.vertexNormals[ 2 ] );\n\n }\n\n }\n\n // restore original normals\n\n for ( f = 0, fl = this.faces.length; f < fl; f ++ ) {\n\n face = this.faces[ f ];\n\n face.normal = face.__originalFaceNormal;\n face.vertexNormals = face.__originalVertexNormals;\n\n }\n\n },\n\n computeBoundingBox: function () {\n\n if ( this.boundingBox === null ) {\n\n this.boundingBox = new Box3();\n\n }\n\n this.boundingBox.setFromPoints( this.vertices );\n\n },\n\n computeBoundingSphere: function () {\n\n if ( this.boundingSphere === null ) {\n\n this.boundingSphere = new Sphere();\n\n }\n\n this.boundingSphere.setFromPoints( this.vertices );\n\n },\n\n merge: function ( geometry, matrix, materialIndexOffset ) {\n\n if ( ! ( geometry && geometry.isGeometry ) ) {\n\n console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry );\n return;\n\n }\n\n var normalMatrix,\n vertexOffset = this.vertices.length,\n vertices1 = this.vertices,\n vertices2 = geometry.vertices,\n faces1 = this.faces,\n faces2 = geometry.faces,\n uvs1 = this.faceVertexUvs[ 0 ],\n uvs2 = geometry.faceVertexUvs[ 0 ],\n colors1 = this.colors,\n colors2 = geometry.colors;\n\n if ( materialIndexOffset === undefined ) materialIndexOffset = 0;\n\n if ( matrix !== undefined ) {\n\n normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n }\n\n // vertices\n\n for ( var i = 0, il = vertices2.length; i < il; i ++ ) {\n\n var vertex = vertices2[ i ];\n\n var vertexCopy = vertex.clone();\n\n if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix );\n\n vertices1.push( vertexCopy );\n\n }\n\n // colors\n\n for ( var i = 0, il = colors2.length; i < il; i ++ ) {\n\n colors1.push( colors2[ i ].clone() );\n\n }\n\n // faces\n\n for ( i = 0, il = faces2.length; i < il; i ++ ) {\n\n var face = faces2[ i ], faceCopy, normal, color,\n faceVertexNormals = face.vertexNormals,\n faceVertexColors = face.vertexColors;\n\n faceCopy = new Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset );\n faceCopy.normal.copy( face.normal );\n\n if ( normalMatrix !== undefined ) {\n\n faceCopy.normal.applyMatrix3( normalMatrix ).normalize();\n\n }\n\n for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) {\n\n normal = faceVertexNormals[ j ].clone();\n\n if ( normalMatrix !== undefined ) {\n\n normal.applyMatrix3( normalMatrix ).normalize();\n\n }\n\n faceCopy.vertexNormals.push( normal );\n\n }\n\n faceCopy.color.copy( face.color );\n\n for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) {\n\n color = faceVertexColors[ j ];\n faceCopy.vertexColors.push( color.clone() );\n\n }\n\n faceCopy.materialIndex = face.materialIndex + materialIndexOffset;\n\n faces1.push( faceCopy );\n\n }\n\n // uvs\n\n for ( i = 0, il = uvs2.length; i < il; i ++ ) {\n\n var uv = uvs2[ i ], uvCopy = [];\n\n if ( uv === undefined ) {\n\n continue;\n\n }\n\n for ( var j = 0, jl = uv.length; j < jl; j ++ ) {\n\n uvCopy.push( uv[ j ].clone() );\n\n }\n\n uvs1.push( uvCopy );\n\n }\n\n },\n\n mergeMesh: function ( mesh ) {\n\n if ( ! ( mesh && mesh.isMesh ) ) {\n\n console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh );\n return;\n\n }\n\n if ( mesh.matrixAutoUpdate ) mesh.updateMatrix();\n\n this.merge( mesh.geometry, mesh.matrix );\n\n },\n\n /*\n * Checks for duplicate vertices with hashmap.\n * Duplicated vertices are removed\n * and faces' vertices are updated.\n */\n\n mergeVertices: function () {\n\n var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique)\n var unique = [], changes = [];\n\n var v, key;\n var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001\n var precision = Math.pow( 10, precisionPoints );\n var i, il, face;\n var indices, j, jl;\n\n for ( i = 0, il = this.vertices.length; i < il; i ++ ) {\n\n v = this.vertices[ i ];\n key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision );\n\n if ( verticesMap[ key ] === undefined ) {\n\n verticesMap[ key ] = i;\n unique.push( this.vertices[ i ] );\n changes[ i ] = unique.length - 1;\n\n } else {\n\n //console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]);\n changes[ i ] = changes[ verticesMap[ key ] ];\n\n }\n\n }\n\n\n // if faces are completely degenerate after merging vertices, we\n // have to remove them from the geometry.\n var faceIndicesToRemove = [];\n\n for ( i = 0, il = this.faces.length; i < il; i ++ ) {\n\n face = this.faces[ i ];\n\n face.a = changes[ face.a ];\n face.b = changes[ face.b ];\n face.c = changes[ face.c ];\n\n indices = [ face.a, face.b, face.c ];\n\n // if any duplicate vertices are found in a Face3\n // we have to remove the face as nothing can be saved\n for ( var n = 0; n < 3; n ++ ) {\n\n if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) {\n\n faceIndicesToRemove.push( i );\n break;\n\n }\n\n }\n\n }\n\n for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) {\n\n var idx = faceIndicesToRemove[ i ];\n\n this.faces.splice( idx, 1 );\n\n for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) {\n\n this.faceVertexUvs[ j ].splice( idx, 1 );\n\n }\n\n }\n\n // Use unique set of vertices\n\n var diff = this.vertices.length - unique.length;\n this.vertices = unique;\n return diff;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.vertices = [];\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n this.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );\n\n }\n\n return this;\n\n },\n\n sortFacesByMaterialIndex: function () {\n\n var faces = this.faces;\n var length = faces.length;\n\n // tag faces\n\n for ( var i = 0; i < length; i ++ ) {\n\n faces[ i ]._id = i;\n\n }\n\n // sort faces\n\n function materialIndexSort( a, b ) {\n\n return a.materialIndex - b.materialIndex;\n\n }\n\n faces.sort( materialIndexSort );\n\n // sort uvs\n\n var uvs1 = this.faceVertexUvs[ 0 ];\n var uvs2 = this.faceVertexUvs[ 1 ];\n\n var newUvs1, newUvs2;\n\n if ( uvs1 && uvs1.length === length ) newUvs1 = [];\n if ( uvs2 && uvs2.length === length ) newUvs2 = [];\n\n for ( var i = 0; i < length; i ++ ) {\n\n var id = faces[ i ]._id;\n\n if ( newUvs1 ) newUvs1.push( uvs1[ id ] );\n if ( newUvs2 ) newUvs2.push( uvs2[ id ] );\n\n }\n\n if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1;\n if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Geometry',\n generator: 'Geometry.toJSON'\n }\n };\n\n // standard Geometry serialization\n\n data.uuid = this.uuid;\n data.type = this.type;\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.parameters !== undefined ) {\n\n var parameters = this.parameters;\n\n for ( var key in parameters ) {\n\n if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n }\n\n return data;\n\n }\n\n var vertices = [];\n\n for ( var i = 0; i < this.vertices.length; i ++ ) {\n\n var vertex = this.vertices[ i ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n var faces = [];\n var normals = [];\n var normalsHash = {};\n var colors = [];\n var colorsHash = {};\n var uvs = [];\n var uvsHash = {};\n\n for ( var i = 0; i < this.faces.length; i ++ ) {\n\n var face = this.faces[ i ];\n\n var hasMaterial = true;\n var hasFaceUv = false; // deprecated\n var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined;\n var hasFaceNormal = face.normal.length() > 0;\n var hasFaceVertexNormal = face.vertexNormals.length > 0;\n var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1;\n var hasFaceVertexColor = face.vertexColors.length > 0;\n\n var faceType = 0;\n\n faceType = setBit( faceType, 0, 0 ); // isQuad\n faceType = setBit( faceType, 1, hasMaterial );\n faceType = setBit( faceType, 2, hasFaceUv );\n faceType = setBit( faceType, 3, hasFaceVertexUv );\n faceType = setBit( faceType, 4, hasFaceNormal );\n faceType = setBit( faceType, 5, hasFaceVertexNormal );\n faceType = setBit( faceType, 6, hasFaceColor );\n faceType = setBit( faceType, 7, hasFaceVertexColor );\n\n faces.push( faceType );\n faces.push( face.a, face.b, face.c );\n faces.push( face.materialIndex );\n\n if ( hasFaceVertexUv ) {\n\n var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ];\n\n faces.push(\n getUvIndex( faceVertexUvs[ 0 ] ),\n getUvIndex( faceVertexUvs[ 1 ] ),\n getUvIndex( faceVertexUvs[ 2 ] )\n );\n\n }\n\n if ( hasFaceNormal ) {\n\n faces.push( getNormalIndex( face.normal ) );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n var vertexNormals = face.vertexNormals;\n\n faces.push(\n getNormalIndex( vertexNormals[ 0 ] ),\n getNormalIndex( vertexNormals[ 1 ] ),\n getNormalIndex( vertexNormals[ 2 ] )\n );\n\n }\n\n if ( hasFaceColor ) {\n\n faces.push( getColorIndex( face.color ) );\n\n }\n\n if ( hasFaceVertexColor ) {\n\n var vertexColors = face.vertexColors;\n\n faces.push(\n getColorIndex( vertexColors[ 0 ] ),\n getColorIndex( vertexColors[ 1 ] ),\n getColorIndex( vertexColors[ 2 ] )\n );\n\n }\n\n }\n\n function setBit( value, position, enabled ) {\n\n return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) );\n\n }\n\n function getNormalIndex( normal ) {\n\n var hash = normal.x.toString() + normal.y.toString() + normal.z.toString();\n\n if ( normalsHash[ hash ] !== undefined ) {\n\n return normalsHash[ hash ];\n\n }\n\n normalsHash[ hash ] = normals.length / 3;\n normals.push( normal.x, normal.y, normal.z );\n\n return normalsHash[ hash ];\n\n }\n\n function getColorIndex( color ) {\n\n var hash = color.r.toString() + color.g.toString() + color.b.toString();\n\n if ( colorsHash[ hash ] !== undefined ) {\n\n return colorsHash[ hash ];\n\n }\n\n colorsHash[ hash ] = colors.length;\n colors.push( color.getHex() );\n\n return colorsHash[ hash ];\n\n }\n\n function getUvIndex( uv ) {\n\n var hash = uv.x.toString() + uv.y.toString();\n\n if ( uvsHash[ hash ] !== undefined ) {\n\n return uvsHash[ hash ];\n\n }\n\n uvsHash[ hash ] = uvs.length / 2;\n uvs.push( uv.x, uv.y );\n\n return uvsHash[ hash ];\n\n }\n\n data.data = {};\n\n data.data.vertices = vertices;\n data.data.normals = normals;\n if ( colors.length > 0 ) data.data.colors = colors;\n if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility\n data.data.faces = faces;\n\n return data;\n\n },\n\n clone: function () {\n\n /*\n // Handle primitives\n\n var parameters = this.parameters;\n\n if ( parameters !== undefined ) {\n\n var values = [];\n\n for ( var key in parameters ) {\n\n values.push( parameters[ key ] );\n\n }\n\n var geometry = Object.create( this.constructor.prototype );\n this.constructor.apply( geometry, values );\n return geometry;\n\n }\n\n return new this.constructor().copy( this );\n */\n\n return new Geometry().copy( this );\n\n },\n\n copy: function ( source ) {\n\n var i, il, j, jl, k, kl;\n\n // reset\n\n this.vertices = [];\n this.colors = [];\n this.faces = [];\n this.faceVertexUvs = [[]];\n this.morphTargets = [];\n this.morphNormals = [];\n this.skinWeights = [];\n this.skinIndices = [];\n this.lineDistances = [];\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // name\n\n this.name = source.name;\n\n // vertices\n\n var vertices = source.vertices;\n\n for ( i = 0, il = vertices.length; i < il; i ++ ) {\n\n this.vertices.push( vertices[ i ].clone() );\n\n }\n\n // colors\n\n var colors = source.colors;\n\n for ( i = 0, il = colors.length; i < il; i ++ ) {\n\n this.colors.push( colors[ i ].clone() );\n\n }\n\n // faces\n\n var faces = source.faces;\n\n for ( i = 0, il = faces.length; i < il; i ++ ) {\n\n this.faces.push( faces[ i ].clone() );\n\n }\n\n // face vertex uvs\n\n for ( i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) {\n\n var faceVertexUvs = source.faceVertexUvs[ i ];\n\n if ( this.faceVertexUvs[ i ] === undefined ) {\n\n this.faceVertexUvs[ i ] = [];\n\n }\n\n for ( j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) {\n\n var uvs = faceVertexUvs[ j ], uvsCopy = [];\n\n for ( k = 0, kl = uvs.length; k < kl; k ++ ) {\n\n var uv = uvs[ k ];\n\n uvsCopy.push( uv.clone() );\n\n }\n\n this.faceVertexUvs[ i ].push( uvsCopy );\n\n }\n\n }\n\n // morph targets\n\n var morphTargets = source.morphTargets;\n\n for ( i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n var morphTarget = {};\n morphTarget.name = morphTargets[ i ].name;\n\n // vertices\n\n if ( morphTargets[ i ].vertices !== undefined ) {\n\n morphTarget.vertices = [];\n\n for ( j = 0, jl = morphTargets[ i ].vertices.length; j < jl; j ++ ) {\n\n morphTarget.vertices.push( morphTargets[ i ].vertices[ j ].clone() );\n\n }\n\n }\n\n // normals\n\n if ( morphTargets[ i ].normals !== undefined ) {\n\n morphTarget.normals = [];\n\n for ( j = 0, jl = morphTargets[ i ].normals.length; j < jl; j ++ ) {\n\n morphTarget.normals.push( morphTargets[ i ].normals[ j ].clone() );\n\n }\n\n }\n\n this.morphTargets.push( morphTarget );\n\n }\n\n // morph normals\n\n var morphNormals = source.morphNormals;\n\n for ( i = 0, il = morphNormals.length; i < il; i ++ ) {\n\n var morphNormal = {};\n\n // vertex normals\n\n if ( morphNormals[ i ].vertexNormals !== undefined ) {\n\n morphNormal.vertexNormals = [];\n\n for ( j = 0, jl = morphNormals[ i ].vertexNormals.length; j < jl; j ++ ) {\n\n var srcVertexNormal = morphNormals[ i ].vertexNormals[ j ];\n var destVertexNormal = {};\n\n destVertexNormal.a = srcVertexNormal.a.clone();\n destVertexNormal.b = srcVertexNormal.b.clone();\n destVertexNormal.c = srcVertexNormal.c.clone();\n\n morphNormal.vertexNormals.push( destVertexNormal );\n\n }\n\n }\n\n // face normals\n\n if ( morphNormals[ i ].faceNormals !== undefined ) {\n\n morphNormal.faceNormals = [];\n\n for ( j = 0, jl = morphNormals[ i ].faceNormals.length; j < jl; j ++ ) {\n\n morphNormal.faceNormals.push( morphNormals[ i ].faceNormals[ j ].clone() );\n\n }\n\n }\n\n this.morphNormals.push( morphNormal );\n\n }\n\n // skin weights\n\n var skinWeights = source.skinWeights;\n\n for ( i = 0, il = skinWeights.length; i < il; i ++ ) {\n\n this.skinWeights.push( skinWeights[ i ].clone() );\n\n }\n\n // skin indices\n\n var skinIndices = source.skinIndices;\n\n for ( i = 0, il = skinIndices.length; i < il; i ++ ) {\n\n this.skinIndices.push( skinIndices[ i ].clone() );\n\n }\n\n // line distances\n\n var lineDistances = source.lineDistances;\n\n for ( i = 0, il = lineDistances.length; i < il; i ++ ) {\n\n this.lineDistances.push( lineDistances[ i ] );\n\n }\n\n // bounding box\n\n var boundingBox = source.boundingBox;\n\n if ( boundingBox !== null ) {\n\n this.boundingBox = boundingBox.clone();\n\n }\n\n // bounding sphere\n\n var boundingSphere = source.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n this.boundingSphere = boundingSphere.clone();\n\n }\n\n // update flags\n\n this.elementsNeedUpdate = source.elementsNeedUpdate;\n this.verticesNeedUpdate = source.verticesNeedUpdate;\n this.uvsNeedUpdate = source.uvsNeedUpdate;\n this.normalsNeedUpdate = source.normalsNeedUpdate;\n this.colorsNeedUpdate = source.colorsNeedUpdate;\n this.lineDistancesNeedUpdate = source.lineDistancesNeedUpdate;\n this.groupsNeedUpdate = source.groupsNeedUpdate;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function BufferAttribute( array, itemSize, normalized ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.name = '';\n\n this.array = array;\n this.itemSize = itemSize;\n this.count = array !== undefined ? array.length / itemSize : 0;\n this.normalized = normalized === true;\n\n this.dynamic = false;\n this.updateRange = { offset: 0, count: - 1 };\n\n this.version = 0;\n\n }\n\n Object.defineProperty( BufferAttribute.prototype, 'needsUpdate', {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n Object.assign( BufferAttribute.prototype, {\n\n isBufferAttribute: true,\n\n onUploadCallback: function () {},\n\n setArray: function ( array ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.count = array !== undefined ? array.length / this.itemSize : 0;\n this.array = array;\n\n },\n\n setDynamic: function ( value ) {\n\n this.dynamic = value;\n\n return this;\n\n },\n\n copy: function ( source ) {\n\n this.array = new source.array.constructor( source.array );\n this.itemSize = source.itemSize;\n this.count = source.count;\n this.normalized = source.normalized;\n\n this.dynamic = source.dynamic;\n\n return this;\n\n },\n\n copyAt: function ( index1, attribute, index2 ) {\n\n index1 *= this.itemSize;\n index2 *= attribute.itemSize;\n\n for ( var i = 0, l = this.itemSize; i < l; i ++ ) {\n\n this.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n }\n\n return this;\n\n },\n\n copyArray: function ( array ) {\n\n this.array.set( array );\n\n return this;\n\n },\n\n copyColorsArray: function ( colors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = colors.length; i < l; i ++ ) {\n\n var color = colors[ i ];\n\n if ( color === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i );\n color = new Color();\n\n }\n\n array[ offset ++ ] = color.r;\n array[ offset ++ ] = color.g;\n array[ offset ++ ] = color.b;\n\n }\n\n return this;\n\n },\n\n copyVector2sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i );\n vector = new Vector2();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n\n }\n\n return this;\n\n },\n\n copyVector3sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i );\n vector = new Vector3();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n array[ offset ++ ] = vector.z;\n\n }\n\n return this;\n\n },\n\n copyVector4sArray: function ( vectors ) {\n\n var array = this.array, offset = 0;\n\n for ( var i = 0, l = vectors.length; i < l; i ++ ) {\n\n var vector = vectors[ i ];\n\n if ( vector === undefined ) {\n\n console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i );\n vector = new Vector4();\n\n }\n\n array[ offset ++ ] = vector.x;\n array[ offset ++ ] = vector.y;\n array[ offset ++ ] = vector.z;\n array[ offset ++ ] = vector.w;\n\n }\n\n return this;\n\n },\n\n set: function ( value, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.array.set( value, offset );\n\n return this;\n\n },\n\n getX: function ( index ) {\n\n return this.array[ index * this.itemSize ];\n\n },\n\n setX: function ( index, x ) {\n\n this.array[ index * this.itemSize ] = x;\n\n return this;\n\n },\n\n getY: function ( index ) {\n\n return this.array[ index * this.itemSize + 1 ];\n\n },\n\n setY: function ( index, y ) {\n\n this.array[ index * this.itemSize + 1 ] = y;\n\n return this;\n\n },\n\n getZ: function ( index ) {\n\n return this.array[ index * this.itemSize + 2 ];\n\n },\n\n setZ: function ( index, z ) {\n\n this.array[ index * this.itemSize + 2 ] = z;\n\n return this;\n\n },\n\n getW: function ( index ) {\n\n return this.array[ index * this.itemSize + 3 ];\n\n },\n\n setW: function ( index, w ) {\n\n this.array[ index * this.itemSize + 3 ] = w;\n\n return this;\n\n },\n\n setXY: function ( index, x, y ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n\n return this;\n\n },\n\n setXYZ: function ( index, x, y, z ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n this.array[ index + 2 ] = z;\n\n return this;\n\n },\n\n setXYZW: function ( index, x, y, z, w ) {\n\n index *= this.itemSize;\n\n this.array[ index + 0 ] = x;\n this.array[ index + 1 ] = y;\n this.array[ index + 2 ] = z;\n this.array[ index + 3 ] = w;\n\n return this;\n\n },\n\n onUpload: function ( callback ) {\n\n this.onUploadCallback = callback;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor( this.array, this.itemSize ).copy( this );\n\n }\n\n } );\n\n //\n\n function Int8BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int8Array( array ), itemSize, normalized );\n\n }\n\n Int8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int8BufferAttribute.prototype.constructor = Int8BufferAttribute;\n\n\n function Uint8BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint8Array( array ), itemSize, normalized );\n\n }\n\n Uint8BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint8BufferAttribute.prototype.constructor = Uint8BufferAttribute;\n\n\n function Uint8ClampedBufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint8ClampedArray( array ), itemSize, normalized );\n\n }\n\n Uint8ClampedBufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint8ClampedBufferAttribute.prototype.constructor = Uint8ClampedBufferAttribute;\n\n\n function Int16BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int16Array( array ), itemSize, normalized );\n\n }\n\n Int16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int16BufferAttribute.prototype.constructor = Int16BufferAttribute;\n\n\n function Uint16BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint16Array( array ), itemSize, normalized );\n\n }\n\n Uint16BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint16BufferAttribute.prototype.constructor = Uint16BufferAttribute;\n\n\n function Int32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Int32Array( array ), itemSize, normalized );\n\n }\n\n Int32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Int32BufferAttribute.prototype.constructor = Int32BufferAttribute;\n\n\n function Uint32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Uint32Array( array ), itemSize, normalized );\n\n }\n\n Uint32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Uint32BufferAttribute.prototype.constructor = Uint32BufferAttribute;\n\n\n function Float32BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Float32Array( array ), itemSize, normalized );\n\n }\n\n Float32BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Float32BufferAttribute.prototype.constructor = Float32BufferAttribute;\n\n\n function Float64BufferAttribute( array, itemSize, normalized ) {\n\n BufferAttribute.call( this, new Float64Array( array ), itemSize, normalized );\n\n }\n\n Float64BufferAttribute.prototype = Object.create( BufferAttribute.prototype );\n Float64BufferAttribute.prototype.constructor = Float64BufferAttribute;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function DirectGeometry() {\n\n this.vertices = [];\n this.normals = [];\n this.colors = [];\n this.uvs = [];\n this.uvs2 = [];\n\n this.groups = [];\n\n this.morphTargets = {};\n\n this.skinWeights = [];\n this.skinIndices = [];\n\n // this.lineDistances = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // update flags\n\n this.verticesNeedUpdate = false;\n this.normalsNeedUpdate = false;\n this.colorsNeedUpdate = false;\n this.uvsNeedUpdate = false;\n this.groupsNeedUpdate = false;\n\n }\n\n Object.assign( DirectGeometry.prototype, {\n\n computeGroups: function ( geometry ) {\n\n var group;\n var groups = [];\n var materialIndex = undefined;\n\n var faces = geometry.faces;\n\n for ( var i = 0; i < faces.length; i ++ ) {\n\n var face = faces[ i ];\n\n // materials\n\n if ( face.materialIndex !== materialIndex ) {\n\n materialIndex = face.materialIndex;\n\n if ( group !== undefined ) {\n\n group.count = ( i * 3 ) - group.start;\n groups.push( group );\n\n }\n\n group = {\n start: i * 3,\n materialIndex: materialIndex\n };\n\n }\n\n }\n\n if ( group !== undefined ) {\n\n group.count = ( i * 3 ) - group.start;\n groups.push( group );\n\n }\n\n this.groups = groups;\n\n },\n\n fromGeometry: function ( geometry ) {\n\n var faces = geometry.faces;\n var vertices = geometry.vertices;\n var faceVertexUvs = geometry.faceVertexUvs;\n\n var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0;\n var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0;\n\n // morphs\n\n var morphTargets = geometry.morphTargets;\n var morphTargetsLength = morphTargets.length;\n\n var morphTargetsPosition;\n\n if ( morphTargetsLength > 0 ) {\n\n morphTargetsPosition = [];\n\n for ( var i = 0; i < morphTargetsLength; i ++ ) {\n\n morphTargetsPosition[ i ] = [];\n\n }\n\n this.morphTargets.position = morphTargetsPosition;\n\n }\n\n var morphNormals = geometry.morphNormals;\n var morphNormalsLength = morphNormals.length;\n\n var morphTargetsNormal;\n\n if ( morphNormalsLength > 0 ) {\n\n morphTargetsNormal = [];\n\n for ( var i = 0; i < morphNormalsLength; i ++ ) {\n\n morphTargetsNormal[ i ] = [];\n\n }\n\n this.morphTargets.normal = morphTargetsNormal;\n\n }\n\n // skins\n\n var skinIndices = geometry.skinIndices;\n var skinWeights = geometry.skinWeights;\n\n var hasSkinIndices = skinIndices.length === vertices.length;\n var hasSkinWeights = skinWeights.length === vertices.length;\n\n //\n\n for ( var i = 0; i < faces.length; i ++ ) {\n\n var face = faces[ i ];\n\n this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] );\n\n var vertexNormals = face.vertexNormals;\n\n if ( vertexNormals.length === 3 ) {\n\n this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] );\n\n } else {\n\n var normal = face.normal;\n\n this.normals.push( normal, normal, normal );\n\n }\n\n var vertexColors = face.vertexColors;\n\n if ( vertexColors.length === 3 ) {\n\n this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] );\n\n } else {\n\n var color = face.color;\n\n this.colors.push( color, color, color );\n\n }\n\n if ( hasFaceVertexUv === true ) {\n\n var vertexUvs = faceVertexUvs[ 0 ][ i ];\n\n if ( vertexUvs !== undefined ) {\n\n this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n } else {\n\n console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i );\n\n this.uvs.push( new Vector2(), new Vector2(), new Vector2() );\n\n }\n\n }\n\n if ( hasFaceVertexUv2 === true ) {\n\n var vertexUvs = faceVertexUvs[ 1 ][ i ];\n\n if ( vertexUvs !== undefined ) {\n\n this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] );\n\n } else {\n\n console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i );\n\n this.uvs2.push( new Vector2(), new Vector2(), new Vector2() );\n\n }\n\n }\n\n // morphs\n\n for ( var j = 0; j < morphTargetsLength; j ++ ) {\n\n var morphTarget = morphTargets[ j ].vertices;\n\n morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] );\n\n }\n\n for ( var j = 0; j < morphNormalsLength; j ++ ) {\n\n var morphNormal = morphNormals[ j ].vertexNormals[ i ];\n\n morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c );\n\n }\n\n // skins\n\n if ( hasSkinIndices ) {\n\n this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] );\n\n }\n\n if ( hasSkinWeights ) {\n\n this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] );\n\n }\n\n }\n\n this.computeGroups( geometry );\n\n this.verticesNeedUpdate = geometry.verticesNeedUpdate;\n this.normalsNeedUpdate = geometry.normalsNeedUpdate;\n this.colorsNeedUpdate = geometry.colorsNeedUpdate;\n this.uvsNeedUpdate = geometry.uvsNeedUpdate;\n this.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function arrayMax( array ) {\n\n if ( array.length === 0 ) return - Infinity;\n\n var max = array[ 0 ];\n\n for ( var i = 1, l = array.length; i < l; ++ i ) {\n\n if ( array[ i ] > max ) max = array[ i ];\n\n }\n\n return max;\n\n }\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n var bufferGeometryId = 1; // BufferGeometry uses odd numbers as Id\n\n function BufferGeometry() {\n\n Object.defineProperty( this, 'id', { value: bufferGeometryId += 2 } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'BufferGeometry';\n\n this.index = null;\n this.attributes = {};\n\n this.morphAttributes = {};\n\n this.groups = [];\n\n this.boundingBox = null;\n this.boundingSphere = null;\n\n this.drawRange = { start: 0, count: Infinity };\n\n }\n\n BufferGeometry.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: BufferGeometry,\n\n isBufferGeometry: true,\n\n getIndex: function () {\n\n return this.index;\n\n },\n\n setIndex: function ( index ) {\n\n if ( Array.isArray( index ) ) {\n\n this.index = new ( arrayMax( index ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( index, 1 );\n\n } else {\n\n this.index = index;\n\n }\n\n },\n\n addAttribute: function ( name, attribute ) {\n\n if ( ! ( attribute && attribute.isBufferAttribute ) && ! ( attribute && attribute.isInterleavedBufferAttribute ) ) {\n\n console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' );\n\n this.addAttribute( name, new BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) );\n\n return;\n\n }\n\n if ( name === 'index' ) {\n\n console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' );\n this.setIndex( attribute );\n\n return;\n\n }\n\n this.attributes[ name ] = attribute;\n\n return this;\n\n },\n\n getAttribute: function ( name ) {\n\n return this.attributes[ name ];\n\n },\n\n removeAttribute: function ( name ) {\n\n delete this.attributes[ name ];\n\n return this;\n\n },\n\n addGroup: function ( start, count, materialIndex ) {\n\n this.groups.push( {\n\n start: start,\n count: count,\n materialIndex: materialIndex !== undefined ? materialIndex : 0\n\n } );\n\n },\n\n clearGroups: function () {\n\n this.groups = [];\n\n },\n\n setDrawRange: function ( start, count ) {\n\n this.drawRange.start = start;\n this.drawRange.count = count;\n\n },\n\n applyMatrix: function ( matrix ) {\n\n var position = this.attributes.position;\n\n if ( position !== undefined ) {\n\n matrix.applyToBufferAttribute( position );\n position.needsUpdate = true;\n\n }\n\n var normal = this.attributes.normal;\n\n if ( normal !== undefined ) {\n\n var normalMatrix = new Matrix3().getNormalMatrix( matrix );\n\n normalMatrix.applyToBufferAttribute( normal );\n normal.needsUpdate = true;\n\n }\n\n if ( this.boundingBox !== null ) {\n\n this.computeBoundingBox();\n\n }\n\n if ( this.boundingSphere !== null ) {\n\n this.computeBoundingSphere();\n\n }\n\n return this;\n\n },\n\n rotateX: function () {\n\n // rotate geometry around world x-axis\n\n var m1 = new Matrix4();\n\n return function rotateX( angle ) {\n\n m1.makeRotationX( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateY: function () {\n\n // rotate geometry around world y-axis\n\n var m1 = new Matrix4();\n\n return function rotateY( angle ) {\n\n m1.makeRotationY( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n rotateZ: function () {\n\n // rotate geometry around world z-axis\n\n var m1 = new Matrix4();\n\n return function rotateZ( angle ) {\n\n m1.makeRotationZ( angle );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n translate: function () {\n\n // translate geometry\n\n var m1 = new Matrix4();\n\n return function translate( x, y, z ) {\n\n m1.makeTranslation( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n scale: function () {\n\n // scale geometry\n\n var m1 = new Matrix4();\n\n return function scale( x, y, z ) {\n\n m1.makeScale( x, y, z );\n\n this.applyMatrix( m1 );\n\n return this;\n\n };\n\n }(),\n\n lookAt: function () {\n\n var obj = new Object3D();\n\n return function lookAt( vector ) {\n\n obj.lookAt( vector );\n\n obj.updateMatrix();\n\n this.applyMatrix( obj.matrix );\n\n };\n\n }(),\n\n center: function () {\n\n var offset = new Vector3();\n\n return function center() {\n\n this.computeBoundingBox();\n\n this.boundingBox.getCenter( offset ).negate();\n\n this.translate( offset.x, offset.y, offset.z );\n\n return this;\n\n };\n\n }(),\n\n setFromObject: function ( object ) {\n\n // console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this );\n\n var geometry = object.geometry;\n\n if ( object.isPoints || object.isLine ) {\n\n var positions = new Float32BufferAttribute( geometry.vertices.length * 3, 3 );\n var colors = new Float32BufferAttribute( geometry.colors.length * 3, 3 );\n\n this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) );\n this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) );\n\n if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) {\n\n var lineDistances = new Float32BufferAttribute( geometry.lineDistances.length, 1 );\n\n this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) );\n\n }\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n } else if ( object.isMesh ) {\n\n if ( geometry && geometry.isGeometry ) {\n\n this.fromGeometry( geometry );\n\n }\n\n }\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n var position = [];\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n position.push( point.x, point.y, point.z || 0 );\n\n }\n\n this.addAttribute( 'position', new Float32BufferAttribute( position, 3 ) );\n\n return this;\n\n },\n\n updateFromObject: function ( object ) {\n\n var geometry = object.geometry;\n\n if ( object.isMesh ) {\n\n var direct = geometry.__directGeometry;\n\n if ( geometry.elementsNeedUpdate === true ) {\n\n direct = undefined;\n geometry.elementsNeedUpdate = false;\n\n }\n\n if ( direct === undefined ) {\n\n return this.fromGeometry( geometry );\n\n }\n\n direct.verticesNeedUpdate = geometry.verticesNeedUpdate;\n direct.normalsNeedUpdate = geometry.normalsNeedUpdate;\n direct.colorsNeedUpdate = geometry.colorsNeedUpdate;\n direct.uvsNeedUpdate = geometry.uvsNeedUpdate;\n direct.groupsNeedUpdate = geometry.groupsNeedUpdate;\n\n geometry.verticesNeedUpdate = false;\n geometry.normalsNeedUpdate = false;\n geometry.colorsNeedUpdate = false;\n geometry.uvsNeedUpdate = false;\n geometry.groupsNeedUpdate = false;\n\n geometry = direct;\n\n }\n\n var attribute;\n\n if ( geometry.verticesNeedUpdate === true ) {\n\n attribute = this.attributes.position;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector3sArray( geometry.vertices );\n attribute.needsUpdate = true;\n\n }\n\n geometry.verticesNeedUpdate = false;\n\n }\n\n if ( geometry.normalsNeedUpdate === true ) {\n\n attribute = this.attributes.normal;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector3sArray( geometry.normals );\n attribute.needsUpdate = true;\n\n }\n\n geometry.normalsNeedUpdate = false;\n\n }\n\n if ( geometry.colorsNeedUpdate === true ) {\n\n attribute = this.attributes.color;\n\n if ( attribute !== undefined ) {\n\n attribute.copyColorsArray( geometry.colors );\n attribute.needsUpdate = true;\n\n }\n\n geometry.colorsNeedUpdate = false;\n\n }\n\n if ( geometry.uvsNeedUpdate ) {\n\n attribute = this.attributes.uv;\n\n if ( attribute !== undefined ) {\n\n attribute.copyVector2sArray( geometry.uvs );\n attribute.needsUpdate = true;\n\n }\n\n geometry.uvsNeedUpdate = false;\n\n }\n\n if ( geometry.lineDistancesNeedUpdate ) {\n\n attribute = this.attributes.lineDistance;\n\n if ( attribute !== undefined ) {\n\n attribute.copyArray( geometry.lineDistances );\n attribute.needsUpdate = true;\n\n }\n\n geometry.lineDistancesNeedUpdate = false;\n\n }\n\n if ( geometry.groupsNeedUpdate ) {\n\n geometry.computeGroups( object.geometry );\n this.groups = geometry.groups;\n\n geometry.groupsNeedUpdate = false;\n\n }\n\n return this;\n\n },\n\n fromGeometry: function ( geometry ) {\n\n geometry.__directGeometry = new DirectGeometry().fromGeometry( geometry );\n\n return this.fromDirectGeometry( geometry.__directGeometry );\n\n },\n\n fromDirectGeometry: function ( geometry ) {\n\n var positions = new Float32Array( geometry.vertices.length * 3 );\n this.addAttribute( 'position', new BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) );\n\n if ( geometry.normals.length > 0 ) {\n\n var normals = new Float32Array( geometry.normals.length * 3 );\n this.addAttribute( 'normal', new BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) );\n\n }\n\n if ( geometry.colors.length > 0 ) {\n\n var colors = new Float32Array( geometry.colors.length * 3 );\n this.addAttribute( 'color', new BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) );\n\n }\n\n if ( geometry.uvs.length > 0 ) {\n\n var uvs = new Float32Array( geometry.uvs.length * 2 );\n this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) );\n\n }\n\n if ( geometry.uvs2.length > 0 ) {\n\n var uvs2 = new Float32Array( geometry.uvs2.length * 2 );\n this.addAttribute( 'uv2', new BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) );\n\n }\n\n // groups\n\n this.groups = geometry.groups;\n\n // morphs\n\n for ( var name in geometry.morphTargets ) {\n\n var array = [];\n var morphTargets = geometry.morphTargets[ name ];\n\n for ( var i = 0, l = morphTargets.length; i < l; i ++ ) {\n\n var morphTarget = morphTargets[ i ];\n\n var attribute = new Float32BufferAttribute( morphTarget.length * 3, 3 );\n\n array.push( attribute.copyVector3sArray( morphTarget ) );\n\n }\n\n this.morphAttributes[ name ] = array;\n\n }\n\n // skinning\n\n if ( geometry.skinIndices.length > 0 ) {\n\n var skinIndices = new Float32BufferAttribute( geometry.skinIndices.length * 4, 4 );\n this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) );\n\n }\n\n if ( geometry.skinWeights.length > 0 ) {\n\n var skinWeights = new Float32BufferAttribute( geometry.skinWeights.length * 4, 4 );\n this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) );\n\n }\n\n //\n\n if ( geometry.boundingSphere !== null ) {\n\n this.boundingSphere = geometry.boundingSphere.clone();\n\n }\n\n if ( geometry.boundingBox !== null ) {\n\n this.boundingBox = geometry.boundingBox.clone();\n\n }\n\n return this;\n\n },\n\n computeBoundingBox: function () {\n\n if ( this.boundingBox === null ) {\n\n this.boundingBox = new Box3();\n\n }\n\n var position = this.attributes.position;\n\n if ( position !== undefined ) {\n\n this.boundingBox.setFromBufferAttribute( position );\n\n } else {\n\n this.boundingBox.makeEmpty();\n\n }\n\n if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) {\n\n console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The \"position\" attribute is likely to have NaN values.', this );\n\n }\n\n },\n\n computeBoundingSphere: function () {\n\n var box = new Box3();\n var vector = new Vector3();\n\n return function computeBoundingSphere() {\n\n if ( this.boundingSphere === null ) {\n\n this.boundingSphere = new Sphere();\n\n }\n\n var position = this.attributes.position;\n\n if ( position ) {\n\n var center = this.boundingSphere.center;\n\n box.setFromBufferAttribute( position );\n box.getCenter( center );\n\n // hoping to find a boundingSphere with a radius smaller than the\n // boundingSphere of the boundingBox: sqrt(3) smaller in the best case\n\n var maxRadiusSq = 0;\n\n for ( var i = 0, il = position.count; i < il; i ++ ) {\n\n vector.x = position.getX( i );\n vector.y = position.getY( i );\n vector.z = position.getZ( i );\n maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) );\n\n }\n\n this.boundingSphere.radius = Math.sqrt( maxRadiusSq );\n\n if ( isNaN( this.boundingSphere.radius ) ) {\n\n console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The \"position\" attribute is likely to have NaN values.', this );\n\n }\n\n }\n\n };\n\n }(),\n\n computeFaceNormals: function () {\n\n // backwards compatibility\n\n },\n\n computeVertexNormals: function () {\n\n var index = this.index;\n var attributes = this.attributes;\n var groups = this.groups;\n\n if ( attributes.position ) {\n\n var positions = attributes.position.array;\n\n if ( attributes.normal === undefined ) {\n\n this.addAttribute( 'normal', new BufferAttribute( new Float32Array( positions.length ), 3 ) );\n\n } else {\n\n // reset existing normals to zero\n\n var array = attributes.normal.array;\n\n for ( var i = 0, il = array.length; i < il; i ++ ) {\n\n array[ i ] = 0;\n\n }\n\n }\n\n var normals = attributes.normal.array;\n\n var vA, vB, vC;\n var pA = new Vector3(), pB = new Vector3(), pC = new Vector3();\n var cb = new Vector3(), ab = new Vector3();\n\n // indexed elements\n\n if ( index ) {\n\n var indices = index.array;\n\n if ( groups.length === 0 ) {\n\n this.addGroup( 0, indices.length );\n\n }\n\n for ( var j = 0, jl = groups.length; j < jl; ++ j ) {\n\n var group = groups[ j ];\n\n var start = group.start;\n var count = group.count;\n\n for ( var i = start, il = start + count; i < il; i += 3 ) {\n\n vA = indices[ i + 0 ] * 3;\n vB = indices[ i + 1 ] * 3;\n vC = indices[ i + 2 ] * 3;\n\n pA.fromArray( positions, vA );\n pB.fromArray( positions, vB );\n pC.fromArray( positions, vC );\n\n cb.subVectors( pC, pB );\n ab.subVectors( pA, pB );\n cb.cross( ab );\n\n normals[ vA ] += cb.x;\n normals[ vA + 1 ] += cb.y;\n normals[ vA + 2 ] += cb.z;\n\n normals[ vB ] += cb.x;\n normals[ vB + 1 ] += cb.y;\n normals[ vB + 2 ] += cb.z;\n\n normals[ vC ] += cb.x;\n normals[ vC + 1 ] += cb.y;\n normals[ vC + 2 ] += cb.z;\n\n }\n\n }\n\n } else {\n\n // non-indexed elements (unconnected triangle soup)\n\n for ( var i = 0, il = positions.length; i < il; i += 9 ) {\n\n pA.fromArray( positions, i );\n pB.fromArray( positions, i + 3 );\n pC.fromArray( positions, i + 6 );\n\n cb.subVectors( pC, pB );\n ab.subVectors( pA, pB );\n cb.cross( ab );\n\n normals[ i ] = cb.x;\n normals[ i + 1 ] = cb.y;\n normals[ i + 2 ] = cb.z;\n\n normals[ i + 3 ] = cb.x;\n normals[ i + 4 ] = cb.y;\n normals[ i + 5 ] = cb.z;\n\n normals[ i + 6 ] = cb.x;\n normals[ i + 7 ] = cb.y;\n normals[ i + 8 ] = cb.z;\n\n }\n\n }\n\n this.normalizeNormals();\n\n attributes.normal.needsUpdate = true;\n\n }\n\n },\n\n merge: function ( geometry, offset ) {\n\n if ( ! ( geometry && geometry.isBufferGeometry ) ) {\n\n console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry );\n return;\n\n }\n\n if ( offset === undefined ) {\n\n offset = 0;\n\n console.warn(\n 'THREE.BufferGeometry.merge(): Overwriting original geometry, starting at offset=0. '\n + 'Use BufferGeometryUtils.mergeBufferGeometries() for lossless merge.'\n );\n\n }\n\n var attributes = this.attributes;\n\n for ( var key in attributes ) {\n\n if ( geometry.attributes[ key ] === undefined ) continue;\n\n var attribute1 = attributes[ key ];\n var attributeArray1 = attribute1.array;\n\n var attribute2 = geometry.attributes[ key ];\n var attributeArray2 = attribute2.array;\n\n var attributeSize = attribute2.itemSize;\n\n for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) {\n\n attributeArray1[ j ] = attributeArray2[ i ];\n\n }\n\n }\n\n return this;\n\n },\n\n normalizeNormals: function () {\n\n var vector = new Vector3();\n\n return function normalizeNormals() {\n\n var normals = this.attributes.normal;\n\n for ( var i = 0, il = normals.count; i < il; i ++ ) {\n\n vector.x = normals.getX( i );\n vector.y = normals.getY( i );\n vector.z = normals.getZ( i );\n\n vector.normalize();\n\n normals.setXYZ( i, vector.x, vector.y, vector.z );\n\n }\n\n };\n\n }(),\n\n toNonIndexed: function () {\n\n if ( this.index === null ) {\n\n console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' );\n return this;\n\n }\n\n var geometry2 = new BufferGeometry();\n\n var indices = this.index.array;\n var attributes = this.attributes;\n\n for ( var name in attributes ) {\n\n var attribute = attributes[ name ];\n\n var array = attribute.array;\n var itemSize = attribute.itemSize;\n\n var array2 = new array.constructor( indices.length * itemSize );\n\n var index = 0, index2 = 0;\n\n for ( var i = 0, l = indices.length; i < l; i ++ ) {\n\n index = indices[ i ] * itemSize;\n\n for ( var j = 0; j < itemSize; j ++ ) {\n\n array2[ index2 ++ ] = array[ index ++ ];\n\n }\n\n }\n\n geometry2.addAttribute( name, new BufferAttribute( array2, itemSize ) );\n\n }\n\n var groups = this.groups;\n\n for ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n geometry2.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n return geometry2;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'BufferGeometry',\n generator: 'BufferGeometry.toJSON'\n }\n };\n\n // standard BufferGeometry serialization\n\n data.uuid = this.uuid;\n data.type = this.type;\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.parameters !== undefined ) {\n\n var parameters = this.parameters;\n\n for ( var key in parameters ) {\n\n if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ];\n\n }\n\n return data;\n\n }\n\n data.data = { attributes: {} };\n\n var index = this.index;\n\n if ( index !== null ) {\n\n var array = Array.prototype.slice.call( index.array );\n\n data.data.index = {\n type: index.array.constructor.name,\n array: array\n };\n\n }\n\n var attributes = this.attributes;\n\n for ( var key in attributes ) {\n\n var attribute = attributes[ key ];\n\n var array = Array.prototype.slice.call( attribute.array );\n\n data.data.attributes[ key ] = {\n itemSize: attribute.itemSize,\n type: attribute.array.constructor.name,\n array: array,\n normalized: attribute.normalized\n };\n\n }\n\n var groups = this.groups;\n\n if ( groups.length > 0 ) {\n\n data.data.groups = JSON.parse( JSON.stringify( groups ) );\n\n }\n\n var boundingSphere = this.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n data.data.boundingSphere = {\n center: boundingSphere.center.toArray(),\n radius: boundingSphere.radius\n };\n\n }\n\n return data;\n\n },\n\n clone: function () {\n\n /*\n // Handle primitives\n\n var parameters = this.parameters;\n\n if ( parameters !== undefined ) {\n\n var values = [];\n\n for ( var key in parameters ) {\n\n values.push( parameters[ key ] );\n\n }\n\n var geometry = Object.create( this.constructor.prototype );\n this.constructor.apply( geometry, values );\n return geometry;\n\n }\n\n return new this.constructor().copy( this );\n */\n\n return new BufferGeometry().copy( this );\n\n },\n\n copy: function ( source ) {\n\n var name, i, l;\n\n // reset\n\n this.index = null;\n this.attributes = {};\n this.morphAttributes = {};\n this.groups = [];\n this.boundingBox = null;\n this.boundingSphere = null;\n\n // name\n\n this.name = source.name;\n\n // index\n\n var index = source.index;\n\n if ( index !== null ) {\n\n this.setIndex( index.clone() );\n\n }\n\n // attributes\n\n var attributes = source.attributes;\n\n for ( name in attributes ) {\n\n var attribute = attributes[ name ];\n this.addAttribute( name, attribute.clone() );\n\n }\n\n // morph attributes\n\n var morphAttributes = source.morphAttributes;\n\n for ( name in morphAttributes ) {\n\n var array = [];\n var morphAttribute = morphAttributes[ name ]; // morphAttribute: array of Float32BufferAttributes\n\n for ( i = 0, l = morphAttribute.length; i < l; i ++ ) {\n\n array.push( morphAttribute[ i ].clone() );\n\n }\n\n this.morphAttributes[ name ] = array;\n\n }\n\n // groups\n\n var groups = source.groups;\n\n for ( i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n this.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n // bounding box\n\n var boundingBox = source.boundingBox;\n\n if ( boundingBox !== null ) {\n\n this.boundingBox = boundingBox.clone();\n\n }\n\n // bounding sphere\n\n var boundingSphere = source.boundingSphere;\n\n if ( boundingSphere !== null ) {\n\n this.boundingSphere = boundingSphere.clone();\n\n }\n\n // draw range\n\n this.drawRange.start = source.drawRange.start;\n this.drawRange.count = source.drawRange.count;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // BoxGeometry\n\n function BoxGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n Geometry.call( this );\n\n this.type = 'BoxGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n depth: depth,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n depthSegments: depthSegments\n };\n\n this.fromBufferGeometry( new BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) );\n this.mergeVertices();\n\n }\n\n BoxGeometry.prototype = Object.create( Geometry.prototype );\n BoxGeometry.prototype.constructor = BoxGeometry;\n\n // BoxBufferGeometry\n\n function BoxBufferGeometry( width, height, depth, widthSegments, heightSegments, depthSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'BoxBufferGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n depth: depth,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n depthSegments: depthSegments\n };\n\n var scope = this;\n\n width = width || 1;\n height = height || 1;\n depth = depth || 1;\n\n // segments\n\n widthSegments = Math.floor( widthSegments ) || 1;\n heightSegments = Math.floor( heightSegments ) || 1;\n depthSegments = Math.floor( depthSegments ) || 1;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var numberOfVertices = 0;\n var groupStart = 0;\n\n // build each side of the box geometry\n\n buildPlane( 'z', 'y', 'x', - 1, - 1, depth, height, width, depthSegments, heightSegments, 0 ); // px\n buildPlane( 'z', 'y', 'x', 1, - 1, depth, height, - width, depthSegments, heightSegments, 1 ); // nx\n buildPlane( 'x', 'z', 'y', 1, 1, width, depth, height, widthSegments, depthSegments, 2 ); // py\n buildPlane( 'x', 'z', 'y', 1, - 1, width, depth, - height, widthSegments, depthSegments, 3 ); // ny\n buildPlane( 'x', 'y', 'z', 1, - 1, width, height, depth, widthSegments, heightSegments, 4 ); // pz\n buildPlane( 'x', 'y', 'z', - 1, - 1, width, height, - depth, widthSegments, heightSegments, 5 ); // nz\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n function buildPlane( u, v, w, udir, vdir, width, height, depth, gridX, gridY, materialIndex ) {\n\n var segmentWidth = width / gridX;\n var segmentHeight = height / gridY;\n\n var widthHalf = width / 2;\n var heightHalf = height / 2;\n var depthHalf = depth / 2;\n\n var gridX1 = gridX + 1;\n var gridY1 = gridY + 1;\n\n var vertexCounter = 0;\n var groupCount = 0;\n\n var ix, iy;\n\n var vector = new Vector3();\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy < gridY1; iy ++ ) {\n\n var y = iy * segmentHeight - heightHalf;\n\n for ( ix = 0; ix < gridX1; ix ++ ) {\n\n var x = ix * segmentWidth - widthHalf;\n\n // set values to correct vector component\n\n vector[ u ] = x * udir;\n vector[ v ] = y * vdir;\n vector[ w ] = depthHalf;\n\n // now apply vector to vertex buffer\n\n vertices.push( vector.x, vector.y, vector.z );\n\n // set values to correct vector component\n\n vector[ u ] = 0;\n vector[ v ] = 0;\n vector[ w ] = depth > 0 ? 1 : - 1;\n\n // now apply vector to normal buffer\n\n normals.push( vector.x, vector.y, vector.z );\n\n // uvs\n\n uvs.push( ix / gridX );\n uvs.push( 1 - ( iy / gridY ) );\n\n // counters\n\n vertexCounter += 1;\n\n }\n\n }\n\n // indices\n\n // 1. you need three indices to draw a single face\n // 2. a single segment consists of two faces\n // 3. so we need to generate six (2*3) indices per segment\n\n for ( iy = 0; iy < gridY; iy ++ ) {\n\n for ( ix = 0; ix < gridX; ix ++ ) {\n\n var a = numberOfVertices + ix + gridX1 * iy;\n var b = numberOfVertices + ix + gridX1 * ( iy + 1 );\n var c = numberOfVertices + ( ix + 1 ) + gridX1 * ( iy + 1 );\n var d = numberOfVertices + ( ix + 1 ) + gridX1 * iy;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n // increase counter\n\n groupCount += 6;\n\n }\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, materialIndex );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n // update total number of vertices\n\n numberOfVertices += vertexCounter;\n\n }\n\n }\n\n BoxBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n BoxBufferGeometry.prototype.constructor = BoxBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // PlaneGeometry\n\n function PlaneGeometry( width, height, widthSegments, heightSegments ) {\n\n Geometry.call( this );\n\n this.type = 'PlaneGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n widthSegments: widthSegments,\n heightSegments: heightSegments\n };\n\n this.fromBufferGeometry( new PlaneBufferGeometry( width, height, widthSegments, heightSegments ) );\n this.mergeVertices();\n\n }\n\n PlaneGeometry.prototype = Object.create( Geometry.prototype );\n PlaneGeometry.prototype.constructor = PlaneGeometry;\n\n // PlaneBufferGeometry\n\n function PlaneBufferGeometry( width, height, widthSegments, heightSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'PlaneBufferGeometry';\n\n this.parameters = {\n width: width,\n height: height,\n widthSegments: widthSegments,\n heightSegments: heightSegments\n };\n\n width = width || 1;\n height = height || 1;\n\n var width_half = width / 2;\n var height_half = height / 2;\n\n var gridX = Math.floor( widthSegments ) || 1;\n var gridY = Math.floor( heightSegments ) || 1;\n\n var gridX1 = gridX + 1;\n var gridY1 = gridY + 1;\n\n var segment_width = width / gridX;\n var segment_height = height / gridY;\n\n var ix, iy;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy < gridY1; iy ++ ) {\n\n var y = iy * segment_height - height_half;\n\n for ( ix = 0; ix < gridX1; ix ++ ) {\n\n var x = ix * segment_width - width_half;\n\n vertices.push( x, - y, 0 );\n\n normals.push( 0, 0, 1 );\n\n uvs.push( ix / gridX );\n uvs.push( 1 - ( iy / gridY ) );\n\n }\n\n }\n\n // indices\n\n for ( iy = 0; iy < gridY; iy ++ ) {\n\n for ( ix = 0; ix < gridX; ix ++ ) {\n\n var a = ix + gridX1 * iy;\n var b = ix + gridX1 * ( iy + 1 );\n var c = ( ix + 1 ) + gridX1 * ( iy + 1 );\n var d = ( ix + 1 ) + gridX1 * iy;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n PlaneBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n PlaneBufferGeometry.prototype.constructor = PlaneBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n var materialId = 0;\n\n function Material() {\n\n Object.defineProperty( this, 'id', { value: materialId ++ } );\n\n this.uuid = _Math.generateUUID();\n\n this.name = '';\n this.type = 'Material';\n\n this.fog = true;\n this.lights = true;\n\n this.blending = NormalBlending;\n this.side = FrontSide;\n this.flatShading = false;\n this.vertexColors = NoColors; // THREE.NoColors, THREE.VertexColors, THREE.FaceColors\n\n this.opacity = 1;\n this.transparent = false;\n\n this.blendSrc = SrcAlphaFactor;\n this.blendDst = OneMinusSrcAlphaFactor;\n this.blendEquation = AddEquation;\n this.blendSrcAlpha = null;\n this.blendDstAlpha = null;\n this.blendEquationAlpha = null;\n\n this.depthFunc = LessEqualDepth;\n this.depthTest = true;\n this.depthWrite = true;\n\n this.clippingPlanes = null;\n this.clipIntersection = false;\n this.clipShadows = false;\n\n this.shadowSide = null;\n\n this.colorWrite = true;\n\n this.precision = null; // override the renderer's default precision for this material\n\n this.polygonOffset = false;\n this.polygonOffsetFactor = 0;\n this.polygonOffsetUnits = 0;\n\n this.dithering = false;\n\n this.alphaTest = 0;\n this.premultipliedAlpha = false;\n\n this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer\n\n this.visible = true;\n\n this.userData = {};\n\n this.needsUpdate = true;\n\n }\n\n Material.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: Material,\n\n isMaterial: true,\n\n onBeforeCompile: function () {},\n\n setValues: function ( values ) {\n\n if ( values === undefined ) return;\n\n for ( var key in values ) {\n\n var newValue = values[ key ];\n\n if ( newValue === undefined ) {\n\n console.warn( \"THREE.Material: '\" + key + \"' parameter is undefined.\" );\n continue;\n\n }\n\n // for backward compatability if shading is set in the constructor\n if ( key === 'shading' ) {\n\n console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n this.flatShading = ( newValue === FlatShading ) ? true : false;\n continue;\n\n }\n\n var currentValue = this[ key ];\n\n if ( currentValue === undefined ) {\n\n console.warn( \"THREE.\" + this.type + \": '\" + key + \"' is not a property of this material.\" );\n continue;\n\n }\n\n if ( currentValue && currentValue.isColor ) {\n\n currentValue.set( newValue );\n\n } else if ( ( currentValue && currentValue.isVector3 ) && ( newValue && newValue.isVector3 ) ) {\n\n currentValue.copy( newValue );\n\n } else if ( key === 'overdraw' ) {\n\n // ensure overdraw is backwards-compatible with legacy boolean type\n this[ key ] = Number( newValue );\n\n } else {\n\n this[ key ] = newValue;\n\n }\n\n }\n\n },\n\n toJSON: function ( meta ) {\n\n var isRoot = ( meta === undefined || typeof meta === 'string' );\n\n if ( isRoot ) {\n\n meta = {\n textures: {},\n images: {}\n };\n\n }\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Material',\n generator: 'Material.toJSON'\n }\n };\n\n // standard Material serialization\n data.uuid = this.uuid;\n data.type = this.type;\n\n if ( this.name !== '' ) data.name = this.name;\n\n if ( this.color && this.color.isColor ) data.color = this.color.getHex();\n\n if ( this.roughness !== undefined ) data.roughness = this.roughness;\n if ( this.metalness !== undefined ) data.metalness = this.metalness;\n\n if ( this.emissive && this.emissive.isColor ) data.emissive = this.emissive.getHex();\n if ( this.emissiveIntensity !== 1 ) data.emissiveIntensity = this.emissiveIntensity;\n\n if ( this.specular && this.specular.isColor ) data.specular = this.specular.getHex();\n if ( this.shininess !== undefined ) data.shininess = this.shininess;\n if ( this.clearCoat !== undefined ) data.clearCoat = this.clearCoat;\n if ( this.clearCoatRoughness !== undefined ) data.clearCoatRoughness = this.clearCoatRoughness;\n\n if ( this.map && this.map.isTexture ) data.map = this.map.toJSON( meta ).uuid;\n if ( this.alphaMap && this.alphaMap.isTexture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid;\n if ( this.lightMap && this.lightMap.isTexture ) data.lightMap = this.lightMap.toJSON( meta ).uuid;\n if ( this.bumpMap && this.bumpMap.isTexture ) {\n\n data.bumpMap = this.bumpMap.toJSON( meta ).uuid;\n data.bumpScale = this.bumpScale;\n\n }\n if ( this.normalMap && this.normalMap.isTexture ) {\n\n data.normalMap = this.normalMap.toJSON( meta ).uuid;\n data.normalScale = this.normalScale.toArray();\n\n }\n if ( this.displacementMap && this.displacementMap.isTexture ) {\n\n data.displacementMap = this.displacementMap.toJSON( meta ).uuid;\n data.displacementScale = this.displacementScale;\n data.displacementBias = this.displacementBias;\n\n }\n if ( this.roughnessMap && this.roughnessMap.isTexture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid;\n if ( this.metalnessMap && this.metalnessMap.isTexture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid;\n\n if ( this.emissiveMap && this.emissiveMap.isTexture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid;\n if ( this.specularMap && this.specularMap.isTexture ) data.specularMap = this.specularMap.toJSON( meta ).uuid;\n\n if ( this.envMap && this.envMap.isTexture ) {\n\n data.envMap = this.envMap.toJSON( meta ).uuid;\n data.reflectivity = this.reflectivity; // Scale behind envMap\n\n }\n\n if ( this.gradientMap && this.gradientMap.isTexture ) {\n\n data.gradientMap = this.gradientMap.toJSON( meta ).uuid;\n\n }\n\n if ( this.size !== undefined ) data.size = this.size;\n if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;\n\n if ( this.blending !== NormalBlending ) data.blending = this.blending;\n if ( this.flatShading === true ) data.flatShading = this.flatShading;\n if ( this.side !== FrontSide ) data.side = this.side;\n if ( this.vertexColors !== NoColors ) data.vertexColors = this.vertexColors;\n\n if ( this.opacity < 1 ) data.opacity = this.opacity;\n if ( this.transparent === true ) data.transparent = this.transparent;\n\n data.depthFunc = this.depthFunc;\n data.depthTest = this.depthTest;\n data.depthWrite = this.depthWrite;\n\n // rotation (SpriteMaterial)\n if ( this.rotation !== 0 ) data.rotation = this.rotation;\n\n if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;\n if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;\n if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;\n if ( this.scale !== undefined ) data.scale = this.scale;\n\n if ( this.dithering === true ) data.dithering = true;\n\n if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;\n if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;\n\n if ( this.wireframe === true ) data.wireframe = this.wireframe;\n if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;\n if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;\n if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;\n\n if ( this.morphTargets === true ) data.morphTargets = true;\n if ( this.skinning === true ) data.skinning = true;\n\n if ( this.visible === false ) data.visible = false;\n if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;\n\n // TODO: Copied from Object3D.toJSON\n\n function extractFromCache( cache ) {\n\n var values = [];\n\n for ( var key in cache ) {\n\n var data = cache[ key ];\n delete data.metadata;\n values.push( data );\n\n }\n\n return values;\n\n }\n\n if ( isRoot ) {\n\n var textures = extractFromCache( meta.textures );\n var images = extractFromCache( meta.images );\n\n if ( textures.length > 0 ) data.textures = textures;\n if ( images.length > 0 ) data.images = images;\n\n }\n\n return data;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.name = source.name;\n\n this.fog = source.fog;\n this.lights = source.lights;\n\n this.blending = source.blending;\n this.side = source.side;\n this.flatShading = source.flatShading;\n this.vertexColors = source.vertexColors;\n\n this.opacity = source.opacity;\n this.transparent = source.transparent;\n\n this.blendSrc = source.blendSrc;\n this.blendDst = source.blendDst;\n this.blendEquation = source.blendEquation;\n this.blendSrcAlpha = source.blendSrcAlpha;\n this.blendDstAlpha = source.blendDstAlpha;\n this.blendEquationAlpha = source.blendEquationAlpha;\n\n this.depthFunc = source.depthFunc;\n this.depthTest = source.depthTest;\n this.depthWrite = source.depthWrite;\n\n this.colorWrite = source.colorWrite;\n\n this.precision = source.precision;\n\n this.polygonOffset = source.polygonOffset;\n this.polygonOffsetFactor = source.polygonOffsetFactor;\n this.polygonOffsetUnits = source.polygonOffsetUnits;\n\n this.dithering = source.dithering;\n\n this.alphaTest = source.alphaTest;\n this.premultipliedAlpha = source.premultipliedAlpha;\n\n this.overdraw = source.overdraw;\n\n this.visible = source.visible;\n this.userData = JSON.parse( JSON.stringify( source.userData ) );\n\n this.clipShadows = source.clipShadows;\n this.clipIntersection = source.clipIntersection;\n\n var srcPlanes = source.clippingPlanes,\n dstPlanes = null;\n\n if ( srcPlanes !== null ) {\n\n var n = srcPlanes.length;\n dstPlanes = new Array( n );\n\n for ( var i = 0; i !== n; ++ i )\n dstPlanes[ i ] = srcPlanes[ i ].clone();\n\n }\n\n this.clippingPlanes = dstPlanes;\n\n this.shadowSide = source.shadowSide;\n\n return this;\n\n },\n\n dispose: function () {\n\n this.dispatchEvent( { type: 'dispose' } );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * depthTest: <bool>,\n * depthWrite: <bool>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>\n * }\n */\n\n function MeshBasicMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshBasicMaterial';\n\n this.color = new Color( 0xffffff ); // emissive\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshBasicMaterial.prototype = Object.create( Material.prototype );\n MeshBasicMaterial.prototype.constructor = MeshBasicMaterial;\n\n MeshBasicMaterial.prototype.isMeshBasicMaterial = true;\n\n MeshBasicMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * defines: { \"label\" : \"value\" },\n * uniforms: { \"parameter1\": { value: 1.0 }, \"parameter2\": { value2: 2 } },\n *\n * fragmentShader: <string>,\n * vertexShader: <string>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * lights: <bool>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function ShaderMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'ShaderMaterial';\n\n this.defines = {};\n this.uniforms = {};\n\n this.vertexShader = 'void main() {\\n\\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\\n}';\n this.fragmentShader = 'void main() {\\n\\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\\n}';\n\n this.linewidth = 1;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false; // set to use scene fog\n this.lights = false; // set to use scene lights\n this.clipping = false; // set to use user-defined clipping planes\n\n this.skinning = false; // set to use skinning attribute streams\n this.morphTargets = false; // set to use morph targets\n this.morphNormals = false; // set to use morph normals\n\n this.extensions = {\n derivatives: false, // set to use derivatives\n fragDepth: false, // set to use fragment depth values\n drawBuffers: false, // set to use draw buffers\n shaderTextureLOD: false // set to use shader texture LOD\n };\n\n // When rendered geometry doesn't include these attributes but the material does,\n // use these default values in WebGL. This avoids errors when buffer data is missing.\n this.defaultAttributeValues = {\n 'color': [ 1, 1, 1 ],\n 'uv': [ 0, 0 ],\n 'uv2': [ 0, 0 ]\n };\n\n this.index0AttributeName = undefined;\n this.uniformsNeedUpdate = false;\n\n if ( parameters !== undefined ) {\n\n if ( parameters.attributes !== undefined ) {\n\n console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' );\n\n }\n\n this.setValues( parameters );\n\n }\n\n }\n\n ShaderMaterial.prototype = Object.create( Material.prototype );\n ShaderMaterial.prototype.constructor = ShaderMaterial;\n\n ShaderMaterial.prototype.isShaderMaterial = true;\n\n ShaderMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.fragmentShader = source.fragmentShader;\n this.vertexShader = source.vertexShader;\n\n this.uniforms = UniformsUtils.clone( source.uniforms );\n\n this.defines = source.defines;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n this.lights = source.lights;\n this.clipping = source.clipping;\n\n this.skinning = source.skinning;\n\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n this.extensions = source.extensions;\n\n return this;\n\n };\n\n ShaderMaterial.prototype.toJSON = function ( meta ) {\n\n var data = Material.prototype.toJSON.call( this, meta );\n\n data.uniforms = this.uniforms;\n data.vertexShader = this.vertexShader;\n data.fragmentShader = this.fragmentShader;\n\n return data;\n\n };\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Ray( origin, direction ) {\n\n this.origin = ( origin !== undefined ) ? origin : new Vector3();\n this.direction = ( direction !== undefined ) ? direction : new Vector3();\n\n }\n\n Object.assign( Ray.prototype, {\n\n set: function ( origin, direction ) {\n\n this.origin.copy( origin );\n this.direction.copy( direction );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( ray ) {\n\n this.origin.copy( ray.origin );\n this.direction.copy( ray.direction );\n\n return this;\n\n },\n\n at: function ( t, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Ray: .at() target is now required' );\n target = new Vector3();\n\n }\n\n return target.copy( this.direction ).multiplyScalar( t ).add( this.origin );\n\n },\n\n lookAt: function ( v ) {\n\n this.direction.copy( v ).sub( this.origin ).normalize();\n\n return this;\n\n },\n\n recast: function () {\n\n var v1 = new Vector3();\n\n return function recast( t ) {\n\n this.origin.copy( this.at( t, v1 ) );\n\n return this;\n\n };\n\n }(),\n\n closestPointToPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Ray: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n target.subVectors( point, this.origin );\n\n var directionDistance = target.dot( this.direction );\n\n if ( directionDistance < 0 ) {\n\n return target.copy( this.origin );\n\n }\n\n return target.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n },\n\n distanceToPoint: function ( point ) {\n\n return Math.sqrt( this.distanceSqToPoint( point ) );\n\n },\n\n distanceSqToPoint: function () {\n\n var v1 = new Vector3();\n\n return function distanceSqToPoint( point ) {\n\n var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction );\n\n // point behind the ray\n\n if ( directionDistance < 0 ) {\n\n return this.origin.distanceToSquared( point );\n\n }\n\n v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin );\n\n return v1.distanceToSquared( point );\n\n };\n\n }(),\n\n distanceSqToSegment: function () {\n\n var segCenter = new Vector3();\n var segDir = new Vector3();\n var diff = new Vector3();\n\n return function distanceSqToSegment( v0, v1, optionalPointOnRay, optionalPointOnSegment ) {\n\n // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteDistRaySegment.h\n // It returns the min distance between the ray and the segment\n // defined by v0 and v1\n // It can also set two optional targets :\n // - The closest point on the ray\n // - The closest point on the segment\n\n segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 );\n segDir.copy( v1 ).sub( v0 ).normalize();\n diff.copy( this.origin ).sub( segCenter );\n\n var segExtent = v0.distanceTo( v1 ) * 0.5;\n var a01 = - this.direction.dot( segDir );\n var b0 = diff.dot( this.direction );\n var b1 = - diff.dot( segDir );\n var c = diff.lengthSq();\n var det = Math.abs( 1 - a01 * a01 );\n var s0, s1, sqrDist, extDet;\n\n if ( det > 0 ) {\n\n // The ray and segment are not parallel.\n\n s0 = a01 * b1 - b0;\n s1 = a01 * b0 - b1;\n extDet = segExtent * det;\n\n if ( s0 >= 0 ) {\n\n if ( s1 >= - extDet ) {\n\n if ( s1 <= extDet ) {\n\n // region 0\n // Minimum at interior points of ray and segment.\n\n var invDet = 1 / det;\n s0 *= invDet;\n s1 *= invDet;\n sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c;\n\n } else {\n\n // region 1\n\n s1 = segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n } else {\n\n // region 5\n\n s1 = - segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n } else {\n\n if ( s1 <= - extDet ) {\n\n // region 4\n\n s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) );\n s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n } else if ( s1 <= extDet ) {\n\n // region 3\n\n s0 = 0;\n s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = s1 * ( s1 + 2 * b1 ) + c;\n\n } else {\n\n // region 2\n\n s0 = Math.max( 0, - ( a01 * segExtent + b0 ) );\n s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n }\n\n } else {\n\n // Ray and segment are parallel.\n\n s1 = ( a01 > 0 ) ? - segExtent : segExtent;\n s0 = Math.max( 0, - ( a01 * s1 + b0 ) );\n sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c;\n\n }\n\n if ( optionalPointOnRay ) {\n\n optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin );\n\n }\n\n if ( optionalPointOnSegment ) {\n\n optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter );\n\n }\n\n return sqrDist;\n\n };\n\n }(),\n\n intersectSphere: function () {\n\n var v1 = new Vector3();\n\n return function intersectSphere( sphere, target ) {\n\n v1.subVectors( sphere.center, this.origin );\n var tca = v1.dot( this.direction );\n var d2 = v1.dot( v1 ) - tca * tca;\n var radius2 = sphere.radius * sphere.radius;\n\n if ( d2 > radius2 ) return null;\n\n var thc = Math.sqrt( radius2 - d2 );\n\n // t0 = first intersect point - entrance on front of sphere\n var t0 = tca - thc;\n\n // t1 = second intersect point - exit point on back of sphere\n var t1 = tca + thc;\n\n // test to see if both t0 and t1 are behind the ray - if so, return null\n if ( t0 < 0 && t1 < 0 ) return null;\n\n // test to see if t0 is behind the ray:\n // if it is, the ray is inside the sphere, so return the second exit point scaled by t1,\n // in order to always return an intersect point that is in front of the ray.\n if ( t0 < 0 ) return this.at( t1, target );\n\n // else t0 is in front of the ray, so return the first collision point scaled by t0\n return this.at( t0, target );\n\n };\n\n }(),\n\n intersectsSphere: function ( sphere ) {\n\n return this.distanceToPoint( sphere.center ) <= sphere.radius;\n\n },\n\n distanceToPlane: function ( plane ) {\n\n var denominator = plane.normal.dot( this.direction );\n\n if ( denominator === 0 ) {\n\n // line is coplanar, return origin\n if ( plane.distanceToPoint( this.origin ) === 0 ) {\n\n return 0;\n\n }\n\n // Null is preferable to undefined since undefined means.... it is undefined\n\n return null;\n\n }\n\n var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator;\n\n // Return if the ray never intersects the plane\n\n return t >= 0 ? t : null;\n\n },\n\n intersectPlane: function ( plane, target ) {\n\n var t = this.distanceToPlane( plane );\n\n if ( t === null ) {\n\n return null;\n\n }\n\n return this.at( t, target );\n\n },\n\n intersectsPlane: function ( plane ) {\n\n // check if the ray lies on the plane first\n\n var distToPoint = plane.distanceToPoint( this.origin );\n\n if ( distToPoint === 0 ) {\n\n return true;\n\n }\n\n var denominator = plane.normal.dot( this.direction );\n\n if ( denominator * distToPoint < 0 ) {\n\n return true;\n\n }\n\n // ray origin is behind the plane (and is pointing behind it)\n\n return false;\n\n },\n\n intersectBox: function ( box, target ) {\n\n var tmin, tmax, tymin, tymax, tzmin, tzmax;\n\n var invdirx = 1 / this.direction.x,\n invdiry = 1 / this.direction.y,\n invdirz = 1 / this.direction.z;\n\n var origin = this.origin;\n\n if ( invdirx >= 0 ) {\n\n tmin = ( box.min.x - origin.x ) * invdirx;\n tmax = ( box.max.x - origin.x ) * invdirx;\n\n } else {\n\n tmin = ( box.max.x - origin.x ) * invdirx;\n tmax = ( box.min.x - origin.x ) * invdirx;\n\n }\n\n if ( invdiry >= 0 ) {\n\n tymin = ( box.min.y - origin.y ) * invdiry;\n tymax = ( box.max.y - origin.y ) * invdiry;\n\n } else {\n\n tymin = ( box.max.y - origin.y ) * invdiry;\n tymax = ( box.min.y - origin.y ) * invdiry;\n\n }\n\n if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null;\n\n // These lines also handle the case where tmin or tmax is NaN\n // (result of 0 * Infinity). x !== x returns true if x is NaN\n\n if ( tymin > tmin || tmin !== tmin ) tmin = tymin;\n\n if ( tymax < tmax || tmax !== tmax ) tmax = tymax;\n\n if ( invdirz >= 0 ) {\n\n tzmin = ( box.min.z - origin.z ) * invdirz;\n tzmax = ( box.max.z - origin.z ) * invdirz;\n\n } else {\n\n tzmin = ( box.max.z - origin.z ) * invdirz;\n tzmax = ( box.min.z - origin.z ) * invdirz;\n\n }\n\n if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null;\n\n if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin;\n\n if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax;\n\n //return point closest to the ray (positive side)\n\n if ( tmax < 0 ) return null;\n\n return this.at( tmin >= 0 ? tmin : tmax, target );\n\n },\n\n intersectsBox: ( function () {\n\n var v = new Vector3();\n\n return function intersectsBox( box ) {\n\n return this.intersectBox( box, v ) !== null;\n\n };\n\n } )(),\n\n intersectTriangle: function () {\n\n // Compute the offset origin, edges, and normal.\n var diff = new Vector3();\n var edge1 = new Vector3();\n var edge2 = new Vector3();\n var normal = new Vector3();\n\n return function intersectTriangle( a, b, c, backfaceCulling, target ) {\n\n // from http://www.geometrictools.com/GTEngine/Include/Mathematics/GteIntrRay3Triangle3.h\n\n edge1.subVectors( b, a );\n edge2.subVectors( c, a );\n normal.crossVectors( edge1, edge2 );\n\n // Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction,\n // E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by\n // |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2))\n // |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q))\n // |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N)\n var DdN = this.direction.dot( normal );\n var sign;\n\n if ( DdN > 0 ) {\n\n if ( backfaceCulling ) return null;\n sign = 1;\n\n } else if ( DdN < 0 ) {\n\n sign = - 1;\n DdN = - DdN;\n\n } else {\n\n return null;\n\n }\n\n diff.subVectors( this.origin, a );\n var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) );\n\n // b1 < 0, no intersection\n if ( DdQxE2 < 0 ) {\n\n return null;\n\n }\n\n var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) );\n\n // b2 < 0, no intersection\n if ( DdE1xQ < 0 ) {\n\n return null;\n\n }\n\n // b1+b2 > 1, no intersection\n if ( DdQxE2 + DdE1xQ > DdN ) {\n\n return null;\n\n }\n\n // Line intersects triangle, check if ray does.\n var QdN = - sign * diff.dot( normal );\n\n // t < 0, no intersection\n if ( QdN < 0 ) {\n\n return null;\n\n }\n\n // Ray intersects triangle.\n return this.at( QdN / DdN, target );\n\n };\n\n }(),\n\n applyMatrix4: function ( matrix4 ) {\n\n this.origin.applyMatrix4( matrix4 );\n this.direction.transformDirection( matrix4 );\n\n return this;\n\n },\n\n equals: function ( ray ) {\n\n return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Line3( start, end ) {\n\n this.start = ( start !== undefined ) ? start : new Vector3();\n this.end = ( end !== undefined ) ? end : new Vector3();\n\n }\n\n Object.assign( Line3.prototype, {\n\n set: function ( start, end ) {\n\n this.start.copy( start );\n this.end.copy( end );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( line ) {\n\n this.start.copy( line.start );\n this.end.copy( line.end );\n\n return this;\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .getCenter() target is now required' );\n target = new Vector3();\n\n }\n\n return target.addVectors( this.start, this.end ).multiplyScalar( 0.5 );\n\n },\n\n delta: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .delta() target is now required' );\n target = new Vector3();\n\n }\n\n return target.subVectors( this.end, this.start );\n\n },\n\n distanceSq: function () {\n\n return this.start.distanceToSquared( this.end );\n\n },\n\n distance: function () {\n\n return this.start.distanceTo( this.end );\n\n },\n\n at: function ( t, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .at() target is now required' );\n target = new Vector3();\n\n }\n\n return this.delta( target ).multiplyScalar( t ).add( this.start );\n\n },\n\n closestPointToPointParameter: function () {\n\n var startP = new Vector3();\n var startEnd = new Vector3();\n\n return function closestPointToPointParameter( point, clampToLine ) {\n\n startP.subVectors( point, this.start );\n startEnd.subVectors( this.end, this.start );\n\n var startEnd2 = startEnd.dot( startEnd );\n var startEnd_startP = startEnd.dot( startP );\n\n var t = startEnd_startP / startEnd2;\n\n if ( clampToLine ) {\n\n t = _Math.clamp( t, 0, 1 );\n\n }\n\n return t;\n\n };\n\n }(),\n\n closestPointToPoint: function ( point, clampToLine, target ) {\n\n var t = this.closestPointToPointParameter( point, clampToLine );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Line3: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n return this.delta( target ).multiplyScalar( t ).add( this.start );\n\n },\n\n applyMatrix4: function ( matrix ) {\n\n this.start.applyMatrix4( matrix );\n this.end.applyMatrix4( matrix );\n\n return this;\n\n },\n\n equals: function ( line ) {\n\n return line.start.equals( this.start ) && line.end.equals( this.end );\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Triangle( a, b, c ) {\n\n this.a = ( a !== undefined ) ? a : new Vector3();\n this.b = ( b !== undefined ) ? b : new Vector3();\n this.c = ( c !== undefined ) ? c : new Vector3();\n\n }\n\n Object.assign( Triangle, {\n\n getNormal: function () {\n\n var v0 = new Vector3();\n\n return function getNormal( a, b, c, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getNormal() target is now required' );\n target = new Vector3();\n\n }\n\n target.subVectors( c, b );\n v0.subVectors( a, b );\n target.cross( v0 );\n\n var targetLengthSq = target.lengthSq();\n if ( targetLengthSq > 0 ) {\n\n return target.multiplyScalar( 1 / Math.sqrt( targetLengthSq ) );\n\n }\n\n return target.set( 0, 0, 0 );\n\n };\n\n }(),\n\n // static/instance method to calculate barycentric coordinates\n // based on: http://www.blackpawn.com/texts/pointinpoly/default.html\n getBarycoord: function () {\n\n var v0 = new Vector3();\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function getBarycoord( point, a, b, c, target ) {\n\n v0.subVectors( c, a );\n v1.subVectors( b, a );\n v2.subVectors( point, a );\n\n var dot00 = v0.dot( v0 );\n var dot01 = v0.dot( v1 );\n var dot02 = v0.dot( v2 );\n var dot11 = v1.dot( v1 );\n var dot12 = v1.dot( v2 );\n\n var denom = ( dot00 * dot11 - dot01 * dot01 );\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getBarycoord() target is now required' );\n target = new Vector3();\n\n }\n\n // collinear or singular triangle\n if ( denom === 0 ) {\n\n // arbitrary location outside of triangle?\n // not sure if this is the best idea, maybe should be returning undefined\n return target.set( - 2, - 1, - 1 );\n\n }\n\n var invDenom = 1 / denom;\n var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom;\n var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom;\n\n // barycentric coordinates must always sum to 1\n return target.set( 1 - u - v, v, u );\n\n };\n\n }(),\n\n containsPoint: function () {\n\n var v1 = new Vector3();\n\n return function containsPoint( point, a, b, c ) {\n\n Triangle.getBarycoord( point, a, b, c, v1 );\n\n return ( v1.x >= 0 ) && ( v1.y >= 0 ) && ( ( v1.x + v1.y ) <= 1 );\n\n };\n\n }()\n\n } );\n\n Object.assign( Triangle.prototype, {\n\n set: function ( a, b, c ) {\n\n this.a.copy( a );\n this.b.copy( b );\n this.c.copy( c );\n\n return this;\n\n },\n\n setFromPointsAndIndices: function ( points, i0, i1, i2 ) {\n\n this.a.copy( points[ i0 ] );\n this.b.copy( points[ i1 ] );\n this.c.copy( points[ i2 ] );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( triangle ) {\n\n this.a.copy( triangle.a );\n this.b.copy( triangle.b );\n this.c.copy( triangle.c );\n\n return this;\n\n },\n\n getArea: function () {\n\n var v0 = new Vector3();\n var v1 = new Vector3();\n\n return function getArea() {\n\n v0.subVectors( this.c, this.b );\n v1.subVectors( this.a, this.b );\n\n return v0.cross( v1 ).length() * 0.5;\n\n };\n\n }(),\n\n getMidpoint: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getMidpoint() target is now required' );\n target = new Vector3();\n\n }\n\n return target.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 );\n\n },\n\n getNormal: function ( target ) {\n\n return Triangle.getNormal( this.a, this.b, this.c, target );\n\n },\n\n getPlane: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .getPlane() target is now required' );\n target = new Vector3();\n\n }\n\n return target.setFromCoplanarPoints( this.a, this.b, this.c );\n\n },\n\n getBarycoord: function ( point, target ) {\n\n return Triangle.getBarycoord( point, this.a, this.b, this.c, target );\n\n },\n\n containsPoint: function ( point ) {\n\n return Triangle.containsPoint( point, this.a, this.b, this.c );\n\n },\n\n intersectsBox: function ( box ) {\n\n return box.intersectsTriangle( this );\n\n },\n\n closestPointToPoint: function () {\n\n var plane = new Plane();\n var edgeList = [ new Line3(), new Line3(), new Line3() ];\n var projectedPoint = new Vector3();\n var closestPoint = new Vector3();\n\n return function closestPointToPoint( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Triangle: .closestPointToPoint() target is now required' );\n target = new Vector3();\n\n }\n\n var minDistance = Infinity;\n\n // project the point onto the plane of the triangle\n\n plane.setFromCoplanarPoints( this.a, this.b, this.c );\n plane.projectPoint( point, projectedPoint );\n\n // check if the projection lies within the triangle\n\n if ( this.containsPoint( projectedPoint ) === true ) {\n\n // if so, this is the closest point\n\n target.copy( projectedPoint );\n\n } else {\n\n // if not, the point falls outside the triangle. the target is the closest point to the triangle's edges or vertices\n\n edgeList[ 0 ].set( this.a, this.b );\n edgeList[ 1 ].set( this.b, this.c );\n edgeList[ 2 ].set( this.c, this.a );\n\n for ( var i = 0; i < edgeList.length; i ++ ) {\n\n edgeList[ i ].closestPointToPoint( projectedPoint, true, closestPoint );\n\n var distance = projectedPoint.distanceToSquared( closestPoint );\n\n if ( distance < minDistance ) {\n\n minDistance = distance;\n\n target.copy( closestPoint );\n\n }\n\n }\n\n }\n\n return target;\n\n };\n\n }(),\n\n equals: function ( triangle ) {\n\n return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author mikael emtinger / http://gomo.se/\n * @author jonobr1 / http://jonobr1.com/\n */\n\n function Mesh( geometry, material ) {\n\n Object3D.call( this );\n\n this.type = 'Mesh';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } );\n\n this.drawMode = TrianglesDrawMode;\n\n this.updateMorphTargets();\n\n }\n\n Mesh.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Mesh,\n\n isMesh: true,\n\n setDrawMode: function ( value ) {\n\n this.drawMode = value;\n\n },\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n this.drawMode = source.drawMode;\n\n if ( source.morphTargetInfluences !== undefined ) {\n\n this.morphTargetInfluences = source.morphTargetInfluences.slice();\n\n }\n\n if ( source.morphTargetDictionary !== undefined ) {\n\n this.morphTargetDictionary = Object.assign( {}, source.morphTargetDictionary );\n\n }\n\n return this;\n\n },\n\n updateMorphTargets: function () {\n\n var geometry = this.geometry;\n var m, ml, name;\n\n if ( geometry.isBufferGeometry ) {\n\n var morphAttributes = geometry.morphAttributes;\n var keys = Object.keys( morphAttributes );\n\n if ( keys.length > 0 ) {\n\n var morphAttribute = morphAttributes[ keys[ 0 ] ];\n\n if ( morphAttribute !== undefined ) {\n\n this.morphTargetInfluences = [];\n this.morphTargetDictionary = {};\n\n for ( m = 0, ml = morphAttribute.length; m < ml; m ++ ) {\n\n name = morphAttribute[ m ].name || String( m );\n\n this.morphTargetInfluences.push( 0 );\n this.morphTargetDictionary[ name ] = m;\n\n }\n\n }\n\n }\n\n } else {\n\n var morphTargets = geometry.morphTargets;\n\n if ( morphTargets !== undefined && morphTargets.length > 0 ) {\n\n this.morphTargetInfluences = [];\n this.morphTargetDictionary = {};\n\n for ( m = 0, ml = morphTargets.length; m < ml; m ++ ) {\n\n name = morphTargets[ m ].name || String( m );\n\n this.morphTargetInfluences.push( 0 );\n this.morphTargetDictionary[ name ] = m;\n\n }\n\n }\n\n }\n\n },\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n var vA = new Vector3();\n var vB = new Vector3();\n var vC = new Vector3();\n\n var tempA = new Vector3();\n var tempB = new Vector3();\n var tempC = new Vector3();\n\n var uvA = new Vector2();\n var uvB = new Vector2();\n var uvC = new Vector2();\n\n var barycoord = new Vector3();\n\n var intersectionPoint = new Vector3();\n var intersectionPointWorld = new Vector3();\n\n function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) {\n\n Triangle.getBarycoord( point, p1, p2, p3, barycoord );\n\n uv1.multiplyScalar( barycoord.x );\n uv2.multiplyScalar( barycoord.y );\n uv3.multiplyScalar( barycoord.z );\n\n uv1.add( uv2 ).add( uv3 );\n\n return uv1.clone();\n\n }\n\n function checkIntersection( object, material, raycaster, ray, pA, pB, pC, point ) {\n\n var intersect;\n\n if ( material.side === BackSide ) {\n\n intersect = ray.intersectTriangle( pC, pB, pA, true, point );\n\n } else {\n\n intersect = ray.intersectTriangle( pA, pB, pC, material.side !== DoubleSide, point );\n\n }\n\n if ( intersect === null ) return null;\n\n intersectionPointWorld.copy( point );\n intersectionPointWorld.applyMatrix4( object.matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return null;\n\n return {\n distance: distance,\n point: intersectionPointWorld.clone(),\n object: object\n };\n\n }\n\n function checkBufferGeometryIntersection( object, raycaster, ray, position, uv, a, b, c ) {\n\n vA.fromBufferAttribute( position, a );\n vB.fromBufferAttribute( position, b );\n vC.fromBufferAttribute( position, c );\n\n var intersection = checkIntersection( object, object.material, raycaster, ray, vA, vB, vC, intersectionPoint );\n\n if ( intersection ) {\n\n if ( uv ) {\n\n uvA.fromBufferAttribute( uv, a );\n uvB.fromBufferAttribute( uv, b );\n uvC.fromBufferAttribute( uv, c );\n\n intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC );\n\n }\n\n var face = new Face3( a, b, c );\n Triangle.getNormal( vA, vB, vC, face.normal );\n\n intersection.face = face;\n intersection.faceIndex = a;\n\n }\n\n return intersection;\n\n }\n\n return function raycast( raycaster, intersects ) {\n\n var geometry = this.geometry;\n var material = this.material;\n var matrixWorld = this.matrixWorld;\n\n if ( material === undefined ) return;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n // Check boundingBox before continuing\n\n if ( geometry.boundingBox !== null ) {\n\n if ( ray.intersectsBox( geometry.boundingBox ) === false ) return;\n\n }\n\n var intersection;\n\n if ( geometry.isBufferGeometry ) {\n\n var a, b, c;\n var index = geometry.index;\n var position = geometry.attributes.position;\n var uv = geometry.attributes.uv;\n var i, l;\n\n if ( index !== null ) {\n\n // indexed buffer geometry\n\n for ( i = 0, l = index.count; i < l; i += 3 ) {\n\n a = index.getX( i );\n b = index.getX( i + 1 );\n c = index.getX( i + 2 );\n\n intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );\n\n if ( intersection ) {\n\n intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics\n intersects.push( intersection );\n\n }\n\n }\n\n } else if ( position !== undefined ) {\n\n // non-indexed buffer geometry\n\n for ( i = 0, l = position.count; i < l; i += 3 ) {\n\n a = i;\n b = i + 1;\n c = i + 2;\n\n intersection = checkBufferGeometryIntersection( this, raycaster, ray, position, uv, a, b, c );\n\n if ( intersection ) {\n\n intersection.index = a; // triangle number in positions buffer semantics\n intersects.push( intersection );\n\n }\n\n }\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var fvA, fvB, fvC;\n var isMultiMaterial = Array.isArray( material );\n\n var vertices = geometry.vertices;\n var faces = geometry.faces;\n var uvs;\n\n var faceVertexUvs = geometry.faceVertexUvs[ 0 ];\n if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs;\n\n for ( var f = 0, fl = faces.length; f < fl; f ++ ) {\n\n var face = faces[ f ];\n var faceMaterial = isMultiMaterial ? material[ face.materialIndex ] : material;\n\n if ( faceMaterial === undefined ) continue;\n\n fvA = vertices[ face.a ];\n fvB = vertices[ face.b ];\n fvC = vertices[ face.c ];\n\n if ( faceMaterial.morphTargets === true ) {\n\n var morphTargets = geometry.morphTargets;\n var morphInfluences = this.morphTargetInfluences;\n\n vA.set( 0, 0, 0 );\n vB.set( 0, 0, 0 );\n vC.set( 0, 0, 0 );\n\n for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) {\n\n var influence = morphInfluences[ t ];\n\n if ( influence === 0 ) continue;\n\n var targets = morphTargets[ t ].vertices;\n\n vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence );\n vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence );\n vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence );\n\n }\n\n vA.add( fvA );\n vB.add( fvB );\n vC.add( fvC );\n\n fvA = vA;\n fvB = vB;\n fvC = vC;\n\n }\n\n intersection = checkIntersection( this, faceMaterial, raycaster, ray, fvA, fvB, fvC, intersectionPoint );\n\n if ( intersection ) {\n\n if ( uvs && uvs[ f ] ) {\n\n var uvs_f = uvs[ f ];\n uvA.copy( uvs_f[ 0 ] );\n uvB.copy( uvs_f[ 1 ] );\n uvC.copy( uvs_f[ 2 ] );\n\n intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC );\n\n }\n\n intersection.face = face;\n intersection.faceIndex = f;\n intersects.push( intersection );\n\n }\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLBackground( renderer, state, geometries, premultipliedAlpha ) {\n\n var clearColor = new Color( 0x000000 );\n var clearAlpha = 0;\n\n var planeCamera, planeMesh;\n var boxMesh;\n\n function render( renderList, scene, camera, forceClear ) {\n\n var background = scene.background;\n\n if ( background === null ) {\n\n setClear( clearColor, clearAlpha );\n\n } else if ( background && background.isColor ) {\n\n setClear( background, 1 );\n forceClear = true;\n\n }\n\n if ( renderer.autoClear || forceClear ) {\n\n renderer.clear( renderer.autoClearColor, renderer.autoClearDepth, renderer.autoClearStencil );\n\n }\n\n if ( background && background.isCubeTexture ) {\n\n if ( boxMesh === undefined ) {\n\n boxMesh = new Mesh(\n new BoxBufferGeometry( 1, 1, 1 ),\n new ShaderMaterial( {\n uniforms: ShaderLib.cube.uniforms,\n vertexShader: ShaderLib.cube.vertexShader,\n fragmentShader: ShaderLib.cube.fragmentShader,\n side: BackSide,\n depthTest: true,\n depthWrite: false,\n fog: false\n } )\n );\n\n boxMesh.geometry.removeAttribute( 'normal' );\n boxMesh.geometry.removeAttribute( 'uv' );\n\n boxMesh.onBeforeRender = function ( renderer, scene, camera ) {\n\n this.matrixWorld.copyPosition( camera.matrixWorld );\n\n };\n\n geometries.update( boxMesh.geometry );\n\n }\n\n boxMesh.material.uniforms.tCube.value = background;\n\n renderList.push( boxMesh, boxMesh.geometry, boxMesh.material, 0, null );\n\n } else if ( background && background.isTexture ) {\n\n if ( planeCamera === undefined ) {\n\n planeCamera = new OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );\n\n planeMesh = new Mesh(\n new PlaneBufferGeometry( 2, 2 ),\n new MeshBasicMaterial( { depthTest: false, depthWrite: false, fog: false } )\n );\n\n geometries.update( planeMesh.geometry );\n\n }\n\n planeMesh.material.map = background;\n\n // TODO Push this to renderList\n\n renderer.renderBufferDirect( planeCamera, null, planeMesh.geometry, planeMesh.material, planeMesh, null );\n\n }\n\n }\n\n function setClear( color, alpha ) {\n\n state.buffers.color.setClear( color.r, color.g, color.b, alpha, premultipliedAlpha );\n\n }\n\n return {\n\n getClearColor: function () {\n\n return clearColor;\n\n },\n setClearColor: function ( color, alpha ) {\n\n clearColor.set( color );\n clearAlpha = alpha !== undefined ? alpha : 1;\n setClear( clearColor, clearAlpha );\n\n },\n getClearAlpha: function () {\n\n return clearAlpha;\n\n },\n setClearAlpha: function ( alpha ) {\n\n clearAlpha = alpha;\n setClear( clearColor, clearAlpha );\n\n },\n render: render\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLBufferRenderer( gl, extensions, info ) {\n\n var mode;\n\n function setMode( value ) {\n\n mode = value;\n\n }\n\n function render( start, count ) {\n\n gl.drawArrays( mode, start, count );\n\n info.update( count, mode );\n\n }\n\n function renderInstances( geometry, start, count ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n if ( extension === null ) {\n\n console.error( 'THREE.WebGLBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n var position = geometry.attributes.position;\n\n if ( position.isInterleavedBufferAttribute ) {\n\n count = position.data.count;\n\n extension.drawArraysInstancedANGLE( mode, 0, count, geometry.maxInstancedCount );\n\n } else {\n\n extension.drawArraysInstancedANGLE( mode, start, count, geometry.maxInstancedCount );\n\n }\n\n info.update( count, mode, geometry.maxInstancedCount );\n\n }\n\n //\n\n this.setMode = setMode;\n this.render = render;\n this.renderInstances = renderInstances;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLCapabilities( gl, extensions, parameters ) {\n\n var maxAnisotropy;\n\n function getMaxAnisotropy() {\n\n if ( maxAnisotropy !== undefined ) return maxAnisotropy;\n\n var extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n if ( extension !== null ) {\n\n maxAnisotropy = gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT );\n\n } else {\n\n maxAnisotropy = 0;\n\n }\n\n return maxAnisotropy;\n\n }\n\n function getMaxPrecision( precision ) {\n\n if ( precision === 'highp' ) {\n\n if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.HIGH_FLOAT ).precision > 0 &&\n gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.HIGH_FLOAT ).precision > 0 ) {\n\n return 'highp';\n\n }\n\n precision = 'mediump';\n\n }\n\n if ( precision === 'mediump' ) {\n\n if ( gl.getShaderPrecisionFormat( gl.VERTEX_SHADER, gl.MEDIUM_FLOAT ).precision > 0 &&\n gl.getShaderPrecisionFormat( gl.FRAGMENT_SHADER, gl.MEDIUM_FLOAT ).precision > 0 ) {\n\n return 'mediump';\n\n }\n\n }\n\n return 'lowp';\n\n }\n\n var precision = parameters.precision !== undefined ? parameters.precision : 'highp';\n var maxPrecision = getMaxPrecision( precision );\n\n if ( maxPrecision !== precision ) {\n\n console.warn( 'THREE.WebGLRenderer:', precision, 'not supported, using', maxPrecision, 'instead.' );\n precision = maxPrecision;\n\n }\n\n var logarithmicDepthBuffer = parameters.logarithmicDepthBuffer === true;\n\n var maxTextures = gl.getParameter( gl.MAX_TEXTURE_IMAGE_UNITS );\n var maxVertexTextures = gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS );\n var maxTextureSize = gl.getParameter( gl.MAX_TEXTURE_SIZE );\n var maxCubemapSize = gl.getParameter( gl.MAX_CUBE_MAP_TEXTURE_SIZE );\n\n var maxAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n var maxVertexUniforms = gl.getParameter( gl.MAX_VERTEX_UNIFORM_VECTORS );\n var maxVaryings = gl.getParameter( gl.MAX_VARYING_VECTORS );\n var maxFragmentUniforms = gl.getParameter( gl.MAX_FRAGMENT_UNIFORM_VECTORS );\n\n var vertexTextures = maxVertexTextures > 0;\n var floatFragmentTextures = !! extensions.get( 'OES_texture_float' );\n var floatVertexTextures = vertexTextures && floatFragmentTextures;\n\n return {\n\n getMaxAnisotropy: getMaxAnisotropy,\n getMaxPrecision: getMaxPrecision,\n\n precision: precision,\n logarithmicDepthBuffer: logarithmicDepthBuffer,\n\n maxTextures: maxTextures,\n maxVertexTextures: maxVertexTextures,\n maxTextureSize: maxTextureSize,\n maxCubemapSize: maxCubemapSize,\n\n maxAttributes: maxAttributes,\n maxVertexUniforms: maxVertexUniforms,\n maxVaryings: maxVaryings,\n maxFragmentUniforms: maxFragmentUniforms,\n\n vertexTextures: vertexTextures,\n floatFragmentTextures: floatFragmentTextures,\n floatVertexTextures: floatVertexTextures\n\n };\n\n }\n\n /**\n * @author tschw\n */\n\n function WebGLClipping() {\n\n var scope = this,\n\n globalState = null,\n numGlobalPlanes = 0,\n localClippingEnabled = false,\n renderingShadows = false,\n\n plane = new Plane(),\n viewNormalMatrix = new Matrix3(),\n\n uniform = { value: null, needsUpdate: false };\n\n this.uniform = uniform;\n this.numPlanes = 0;\n this.numIntersection = 0;\n\n this.init = function ( planes, enableLocalClipping, camera ) {\n\n var enabled =\n planes.length !== 0 ||\n enableLocalClipping ||\n // enable state of previous frame - the clipping code has to\n // run another frame in order to reset the state:\n numGlobalPlanes !== 0 ||\n localClippingEnabled;\n\n localClippingEnabled = enableLocalClipping;\n\n globalState = projectPlanes( planes, camera, 0 );\n numGlobalPlanes = planes.length;\n\n return enabled;\n\n };\n\n this.beginShadows = function () {\n\n renderingShadows = true;\n projectPlanes( null );\n\n };\n\n this.endShadows = function () {\n\n renderingShadows = false;\n resetGlobalState();\n\n };\n\n this.setState = function ( planes, clipIntersection, clipShadows, camera, cache, fromCache ) {\n\n if ( ! localClippingEnabled || planes === null || planes.length === 0 || renderingShadows && ! clipShadows ) {\n\n // there's no local clipping\n\n if ( renderingShadows ) {\n\n // there's no global clipping\n\n projectPlanes( null );\n\n } else {\n\n resetGlobalState();\n\n }\n\n } else {\n\n var nGlobal = renderingShadows ? 0 : numGlobalPlanes,\n lGlobal = nGlobal * 4,\n\n dstArray = cache.clippingState || null;\n\n uniform.value = dstArray; // ensure unique state\n\n dstArray = projectPlanes( planes, camera, lGlobal, fromCache );\n\n for ( var i = 0; i !== lGlobal; ++ i ) {\n\n dstArray[ i ] = globalState[ i ];\n\n }\n\n cache.clippingState = dstArray;\n this.numIntersection = clipIntersection ? this.numPlanes : 0;\n this.numPlanes += nGlobal;\n\n }\n\n\n };\n\n function resetGlobalState() {\n\n if ( uniform.value !== globalState ) {\n\n uniform.value = globalState;\n uniform.needsUpdate = numGlobalPlanes > 0;\n\n }\n\n scope.numPlanes = numGlobalPlanes;\n scope.numIntersection = 0;\n\n }\n\n function projectPlanes( planes, camera, dstOffset, skipTransform ) {\n\n var nPlanes = planes !== null ? planes.length : 0,\n dstArray = null;\n\n if ( nPlanes !== 0 ) {\n\n dstArray = uniform.value;\n\n if ( skipTransform !== true || dstArray === null ) {\n\n var flatSize = dstOffset + nPlanes * 4,\n viewMatrix = camera.matrixWorldInverse;\n\n viewNormalMatrix.getNormalMatrix( viewMatrix );\n\n if ( dstArray === null || dstArray.length < flatSize ) {\n\n dstArray = new Float32Array( flatSize );\n\n }\n\n for ( var i = 0, i4 = dstOffset; i !== nPlanes; ++ i, i4 += 4 ) {\n\n plane.copy( planes[ i ] ).applyMatrix4( viewMatrix, viewNormalMatrix );\n\n plane.normal.toArray( dstArray, i4 );\n dstArray[ i4 + 3 ] = plane.constant;\n\n }\n\n }\n\n uniform.value = dstArray;\n uniform.needsUpdate = true;\n\n }\n\n scope.numPlanes = nPlanes;\n\n return dstArray;\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLExtensions( gl ) {\n\n var extensions = {};\n\n return {\n\n get: function ( name ) {\n\n if ( extensions[ name ] !== undefined ) {\n\n return extensions[ name ];\n\n }\n\n var extension;\n\n switch ( name ) {\n\n case 'WEBGL_depth_texture':\n extension = gl.getExtension( 'WEBGL_depth_texture' ) || gl.getExtension( 'MOZ_WEBGL_depth_texture' ) || gl.getExtension( 'WEBKIT_WEBGL_depth_texture' );\n break;\n\n case 'EXT_texture_filter_anisotropic':\n extension = gl.getExtension( 'EXT_texture_filter_anisotropic' ) || gl.getExtension( 'MOZ_EXT_texture_filter_anisotropic' ) || gl.getExtension( 'WEBKIT_EXT_texture_filter_anisotropic' );\n break;\n\n case 'WEBGL_compressed_texture_s3tc':\n extension = gl.getExtension( 'WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'MOZ_WEBGL_compressed_texture_s3tc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_s3tc' );\n break;\n\n case 'WEBGL_compressed_texture_pvrtc':\n extension = gl.getExtension( 'WEBGL_compressed_texture_pvrtc' ) || gl.getExtension( 'WEBKIT_WEBGL_compressed_texture_pvrtc' );\n break;\n\n case 'WEBGL_compressed_texture_etc1':\n extension = gl.getExtension( 'WEBGL_compressed_texture_etc1' );\n break;\n\n default:\n extension = gl.getExtension( name );\n\n }\n\n if ( extension === null ) {\n\n console.warn( 'THREE.WebGLRenderer: ' + name + ' extension not supported.' );\n\n }\n\n extensions[ name ] = extension;\n\n return extension;\n\n }\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLGeometries( gl, attributes, info ) {\n\n var geometries = {};\n var wireframeAttributes = {};\n\n function onGeometryDispose( event ) {\n\n var geometry = event.target;\n var buffergeometry = geometries[ geometry.id ];\n\n if ( buffergeometry.index !== null ) {\n\n attributes.remove( buffergeometry.index );\n\n }\n\n for ( var name in buffergeometry.attributes ) {\n\n attributes.remove( buffergeometry.attributes[ name ] );\n\n }\n\n geometry.removeEventListener( 'dispose', onGeometryDispose );\n\n delete geometries[ geometry.id ];\n\n // TODO Remove duplicate code\n\n var attribute = wireframeAttributes[ geometry.id ];\n\n if ( attribute ) {\n\n attributes.remove( attribute );\n delete wireframeAttributes[ geometry.id ];\n\n }\n\n attribute = wireframeAttributes[ buffergeometry.id ];\n\n if ( attribute ) {\n\n attributes.remove( attribute );\n delete wireframeAttributes[ buffergeometry.id ];\n\n }\n\n //\n\n info.memory.geometries --;\n\n }\n\n function get( object, geometry ) {\n\n var buffergeometry = geometries[ geometry.id ];\n\n if ( buffergeometry ) return buffergeometry;\n\n geometry.addEventListener( 'dispose', onGeometryDispose );\n\n if ( geometry.isBufferGeometry ) {\n\n buffergeometry = geometry;\n\n } else if ( geometry.isGeometry ) {\n\n if ( geometry._bufferGeometry === undefined ) {\n\n geometry._bufferGeometry = new BufferGeometry().setFromObject( object );\n\n }\n\n buffergeometry = geometry._bufferGeometry;\n\n }\n\n geometries[ geometry.id ] = buffergeometry;\n\n info.memory.geometries ++;\n\n return buffergeometry;\n\n }\n\n function update( geometry ) {\n\n var index = geometry.index;\n var geometryAttributes = geometry.attributes;\n\n if ( index !== null ) {\n\n attributes.update( index, gl.ELEMENT_ARRAY_BUFFER );\n\n }\n\n for ( var name in geometryAttributes ) {\n\n attributes.update( geometryAttributes[ name ], gl.ARRAY_BUFFER );\n\n }\n\n // morph targets\n\n var morphAttributes = geometry.morphAttributes;\n\n for ( var name in morphAttributes ) {\n\n var array = morphAttributes[ name ];\n\n for ( var i = 0, l = array.length; i < l; i ++ ) {\n\n attributes.update( array[ i ], gl.ARRAY_BUFFER );\n\n }\n\n }\n\n }\n\n function getWireframeAttribute( geometry ) {\n\n var attribute = wireframeAttributes[ geometry.id ];\n\n if ( attribute ) return attribute;\n\n var indices = [];\n\n var geometryIndex = geometry.index;\n var geometryAttributes = geometry.attributes;\n\n // console.time( 'wireframe' );\n\n if ( geometryIndex !== null ) {\n\n var array = geometryIndex.array;\n\n for ( var i = 0, l = array.length; i < l; i += 3 ) {\n\n var a = array[ i + 0 ];\n var b = array[ i + 1 ];\n var c = array[ i + 2 ];\n\n indices.push( a, b, b, c, c, a );\n\n }\n\n } else {\n\n var array = geometryAttributes.position.array;\n\n for ( var i = 0, l = ( array.length / 3 ) - 1; i < l; i += 3 ) {\n\n var a = i + 0;\n var b = i + 1;\n var c = i + 2;\n\n indices.push( a, b, b, c, c, a );\n\n }\n\n }\n\n // console.timeEnd( 'wireframe' );\n\n attribute = new ( arrayMax( indices ) > 65535 ? Uint32BufferAttribute : Uint16BufferAttribute )( indices, 1 );\n\n attributes.update( attribute, gl.ELEMENT_ARRAY_BUFFER );\n\n wireframeAttributes[ geometry.id ] = attribute;\n\n return attribute;\n\n }\n\n return {\n\n get: get,\n update: update,\n\n getWireframeAttribute: getWireframeAttribute\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLIndexedBufferRenderer( gl, extensions, info ) {\n\n var mode;\n\n function setMode( value ) {\n\n mode = value;\n\n }\n\n var type, bytesPerElement;\n\n function setIndex( value ) {\n\n type = value.type;\n bytesPerElement = value.bytesPerElement;\n\n }\n\n function render( start, count ) {\n\n gl.drawElements( mode, count, type, start * bytesPerElement );\n\n info.update( count, mode );\n\n }\n\n function renderInstances( geometry, start, count ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n if ( extension === null ) {\n\n console.error( 'THREE.WebGLIndexedBufferRenderer: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n extension.drawElementsInstancedANGLE( mode, count, type, start * bytesPerElement, geometry.maxInstancedCount );\n\n info.update( count, mode, geometry.maxInstancedCount );\n\n }\n\n //\n\n this.setMode = setMode;\n this.setIndex = setIndex;\n this.render = render;\n this.renderInstances = renderInstances;\n\n }\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WebGLInfo( gl ) {\n\n var memory = {\n geometries: 0,\n textures: 0\n };\n\n var render = {\n frame: 0,\n calls: 0,\n triangles: 0,\n points: 0,\n lines: 0\n };\n\n function update( count, mode, instanceCount ) {\n\n instanceCount = instanceCount || 1;\n\n render.calls ++;\n\n switch ( mode ) {\n\n case gl.TRIANGLES:\n render.triangles += instanceCount * ( count / 3 );\n break;\n\n case gl.TRIANGLE_STRIP:\n case gl.TRIANGLE_FAN:\n render.triangles += instanceCount * ( count - 2 );\n break;\n\n case gl.LINES:\n render.lines += instanceCount * ( count / 2 );\n break;\n\n case gl.LINE_STRIP:\n render.lines += instanceCount * ( count - 1 );\n break;\n\n case gl.LINE_LOOP:\n render.lines += instanceCount * count;\n break;\n\n case gl.POINTS:\n render.points += instanceCount * count;\n break;\n\n default:\n console.error( 'THREE.WebGLInfo: Unknown draw mode:', mode );\n break;\n\n }\n\n }\n\n function reset() {\n\n render.frame ++;\n render.calls = 0;\n render.triangles = 0;\n render.points = 0;\n render.lines = 0;\n\n }\n\n return {\n memory: memory,\n render: render,\n programs: null,\n autoReset: true,\n reset: reset,\n update: update\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function absNumericalSort( a, b ) {\n\n return Math.abs( b[ 1 ] ) - Math.abs( a[ 1 ] );\n\n }\n\n function WebGLMorphtargets( gl ) {\n\n var influencesList = {};\n var morphInfluences = new Float32Array( 8 );\n\n function update( object, geometry, material, program ) {\n\n var objectInfluences = object.morphTargetInfluences;\n\n var length = objectInfluences.length;\n\n var influences = influencesList[ geometry.id ];\n\n if ( influences === undefined ) {\n\n // initialise list\n\n influences = [];\n\n for ( var i = 0; i < length; i ++ ) {\n\n influences[ i ] = [ i, 0 ];\n\n }\n\n influencesList[ geometry.id ] = influences;\n\n }\n\n var morphTargets = material.morphTargets && geometry.morphAttributes.position;\n var morphNormals = material.morphNormals && geometry.morphAttributes.normal;\n\n // Remove current morphAttributes\n\n for ( var i = 0; i < length; i ++ ) {\n\n var influence = influences[ i ];\n\n if ( influence[ 1 ] !== 0 ) {\n\n if ( morphTargets ) geometry.removeAttribute( 'morphTarget' + i );\n if ( morphNormals ) geometry.removeAttribute( 'morphNormal' + i );\n\n }\n\n }\n\n // Collect influences\n\n for ( var i = 0; i < length; i ++ ) {\n\n var influence = influences[ i ];\n\n influence[ 0 ] = i;\n influence[ 1 ] = objectInfluences[ i ];\n\n }\n\n influences.sort( absNumericalSort );\n\n // Add morphAttributes\n\n for ( var i = 0; i < 8; i ++ ) {\n\n var influence = influences[ i ];\n\n if ( influence ) {\n\n var index = influence[ 0 ];\n var value = influence[ 1 ];\n\n if ( value ) {\n\n if ( morphTargets ) geometry.addAttribute( 'morphTarget' + i, morphTargets[ index ] );\n if ( morphNormals ) geometry.addAttribute( 'morphNormal' + i, morphNormals[ index ] );\n\n morphInfluences[ i ] = value;\n continue;\n\n }\n\n }\n\n morphInfluences[ i ] = 0;\n\n }\n\n program.getUniforms().setValue( gl, 'morphTargetInfluences', morphInfluences );\n\n }\n\n return {\n\n update: update\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLObjects( geometries, info ) {\n\n var updateList = {};\n\n function update( object ) {\n\n var frame = info.render.frame;\n\n var geometry = object.geometry;\n var buffergeometry = geometries.get( object, geometry );\n\n // Update once per frame\n\n if ( updateList[ buffergeometry.id ] !== frame ) {\n\n if ( geometry.isGeometry ) {\n\n buffergeometry.updateFromObject( object );\n\n }\n\n geometries.update( buffergeometry );\n\n updateList[ buffergeometry.id ] = frame;\n\n }\n\n return buffergeometry;\n\n }\n\n function dispose() {\n\n updateList = {};\n\n }\n\n return {\n\n update: update,\n dispose: dispose\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CubeTexture( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding ) {\n\n images = images !== undefined ? images : [];\n mapping = mapping !== undefined ? mapping : CubeReflectionMapping;\n\n Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.flipY = false;\n\n }\n\n CubeTexture.prototype = Object.create( Texture.prototype );\n CubeTexture.prototype.constructor = CubeTexture;\n\n CubeTexture.prototype.isCubeTexture = true;\n\n Object.defineProperty( CubeTexture.prototype, 'images', {\n\n get: function () {\n\n return this.image;\n\n },\n\n set: function ( value ) {\n\n this.image = value;\n\n }\n\n } );\n\n /**\n * @author tschw\n *\n * Uniforms of a program.\n * Those form a tree structure with a special top-level container for the root,\n * which you get by calling 'new WebGLUniforms( gl, program, renderer )'.\n *\n *\n * Properties of inner nodes including the top-level container:\n *\n * .seq - array of nested uniforms\n * .map - nested uniforms by name\n *\n *\n * Methods of all nodes except the top-level container:\n *\n * .setValue( gl, value, [renderer] )\n *\n * uploads a uniform value(s)\n * the 'renderer' parameter is needed for sampler uniforms\n *\n *\n * Static methods of the top-level container (renderer factorizations):\n *\n * .upload( gl, seq, values, renderer )\n *\n * sets uniforms in 'seq' to 'values[id].value'\n *\n * .seqWithValue( seq, values ) : filteredSeq\n *\n * filters 'seq' entries with corresponding entry in values\n *\n *\n * Methods of the top-level container (renderer factorizations):\n *\n * .setValue( gl, name, value )\n *\n * sets uniform with name 'name' to 'value'\n *\n * .set( gl, obj, prop )\n *\n * sets uniform from object and property with same name than uniform\n *\n * .setOptional( gl, obj, prop )\n *\n * like .set for an optional property of the object\n *\n */\n\n var emptyTexture = new Texture();\n var emptyCubeTexture = new CubeTexture();\n\n // --- Base for inner nodes (including the root) ---\n\n function UniformContainer() {\n\n this.seq = [];\n this.map = {};\n\n }\n\n // --- Utilities ---\n\n // Array Caches (provide typed arrays for temporary by size)\n\n var arrayCacheF32 = [];\n var arrayCacheI32 = [];\n\n // Float32Array caches used for uploading Matrix uniforms\n\n var mat4array = new Float32Array( 16 );\n var mat3array = new Float32Array( 9 );\n\n // Flattening for arrays of vectors and matrices\n\n function flatten( array, nBlocks, blockSize ) {\n\n var firstElem = array[ 0 ];\n\n if ( firstElem <= 0 || firstElem > 0 ) return array;\n // unoptimized: ! isNaN( firstElem )\n // see http://jacksondunstan.com/articles/983\n\n var n = nBlocks * blockSize,\n r = arrayCacheF32[ n ];\n\n if ( r === undefined ) {\n\n r = new Float32Array( n );\n arrayCacheF32[ n ] = r;\n\n }\n\n if ( nBlocks !== 0 ) {\n\n firstElem.toArray( r, 0 );\n\n for ( var i = 1, offset = 0; i !== nBlocks; ++ i ) {\n\n offset += blockSize;\n array[ i ].toArray( r, offset );\n\n }\n\n }\n\n return r;\n\n }\n\n // Texture unit allocation\n\n function allocTexUnits( renderer, n ) {\n\n var r = arrayCacheI32[ n ];\n\n if ( r === undefined ) {\n\n r = new Int32Array( n );\n arrayCacheI32[ n ] = r;\n\n }\n\n for ( var i = 0; i !== n; ++ i )\n r[ i ] = renderer.allocTextureUnit();\n\n return r;\n\n }\n\n // --- Setters ---\n\n // Note: Defining these methods externally, because they come in a bunch\n // and this way their names minify.\n\n // Single scalar\n\n function setValue1f( gl, v ) {\n\n gl.uniform1f( this.addr, v );\n\n }\n\n function setValue1i( gl, v ) {\n\n gl.uniform1i( this.addr, v );\n\n }\n\n // Single float vector (from flat array or THREE.VectorN)\n\n function setValue2fv( gl, v ) {\n\n if ( v.x === undefined ) {\n\n gl.uniform2fv( this.addr, v );\n\n } else {\n\n gl.uniform2f( this.addr, v.x, v.y );\n\n }\n\n }\n\n function setValue3fv( gl, v ) {\n\n if ( v.x !== undefined ) {\n\n gl.uniform3f( this.addr, v.x, v.y, v.z );\n\n } else if ( v.r !== undefined ) {\n\n gl.uniform3f( this.addr, v.r, v.g, v.b );\n\n } else {\n\n gl.uniform3fv( this.addr, v );\n\n }\n\n }\n\n function setValue4fv( gl, v ) {\n\n if ( v.x === undefined ) {\n\n gl.uniform4fv( this.addr, v );\n\n } else {\n\n gl.uniform4f( this.addr, v.x, v.y, v.z, v.w );\n\n }\n\n }\n\n // Single matrix (from flat array or MatrixN)\n\n function setValue2fm( gl, v ) {\n\n gl.uniformMatrix2fv( this.addr, false, v.elements || v );\n\n }\n\n function setValue3fm( gl, v ) {\n\n if ( v.elements === undefined ) {\n\n gl.uniformMatrix3fv( this.addr, false, v );\n\n } else {\n\n mat3array.set( v.elements );\n gl.uniformMatrix3fv( this.addr, false, mat3array );\n\n }\n\n }\n\n function setValue4fm( gl, v ) {\n\n if ( v.elements === undefined ) {\n\n gl.uniformMatrix4fv( this.addr, false, v );\n\n } else {\n\n mat4array.set( v.elements );\n gl.uniformMatrix4fv( this.addr, false, mat4array );\n\n }\n\n }\n\n // Single texture (2D / Cube)\n\n function setValueT1( gl, v, renderer ) {\n\n var unit = renderer.allocTextureUnit();\n gl.uniform1i( this.addr, unit );\n renderer.setTexture2D( v || emptyTexture, unit );\n\n }\n\n function setValueT6( gl, v, renderer ) {\n\n var unit = renderer.allocTextureUnit();\n gl.uniform1i( this.addr, unit );\n renderer.setTextureCube( v || emptyCubeTexture, unit );\n\n }\n\n // Integer / Boolean vectors or arrays thereof (always flat arrays)\n\n function setValue2iv( gl, v ) {\n\n gl.uniform2iv( this.addr, v );\n\n }\n\n function setValue3iv( gl, v ) {\n\n gl.uniform3iv( this.addr, v );\n\n }\n\n function setValue4iv( gl, v ) {\n\n gl.uniform4iv( this.addr, v );\n\n }\n\n // Helper to pick the right setter for the singular case\n\n function getSingularSetter( type ) {\n\n switch ( type ) {\n\n case 0x1406: return setValue1f; // FLOAT\n case 0x8b50: return setValue2fv; // _VEC2\n case 0x8b51: return setValue3fv; // _VEC3\n case 0x8b52: return setValue4fv; // _VEC4\n\n case 0x8b5a: return setValue2fm; // _MAT2\n case 0x8b5b: return setValue3fm; // _MAT3\n case 0x8b5c: return setValue4fm; // _MAT4\n\n case 0x8b5e: case 0x8d66: return setValueT1; // SAMPLER_2D, SAMPLER_EXTERNAL_OES\n case 0x8b60: return setValueT6; // SAMPLER_CUBE\n\n case 0x1404: case 0x8b56: return setValue1i; // INT, BOOL\n case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n }\n\n }\n\n // Array of scalars\n\n function setValue1fv( gl, v ) {\n\n gl.uniform1fv( this.addr, v );\n\n }\n function setValue1iv( gl, v ) {\n\n gl.uniform1iv( this.addr, v );\n\n }\n\n // Array of vectors (flat or from THREE classes)\n\n function setValueV2a( gl, v ) {\n\n gl.uniform2fv( this.addr, flatten( v, this.size, 2 ) );\n\n }\n\n function setValueV3a( gl, v ) {\n\n gl.uniform3fv( this.addr, flatten( v, this.size, 3 ) );\n\n }\n\n function setValueV4a( gl, v ) {\n\n gl.uniform4fv( this.addr, flatten( v, this.size, 4 ) );\n\n }\n\n // Array of matrices (flat or from THREE clases)\n\n function setValueM2a( gl, v ) {\n\n gl.uniformMatrix2fv( this.addr, false, flatten( v, this.size, 4 ) );\n\n }\n\n function setValueM3a( gl, v ) {\n\n gl.uniformMatrix3fv( this.addr, false, flatten( v, this.size, 9 ) );\n\n }\n\n function setValueM4a( gl, v ) {\n\n gl.uniformMatrix4fv( this.addr, false, flatten( v, this.size, 16 ) );\n\n }\n\n // Array of textures (2D / Cube)\n\n function setValueT1a( gl, v, renderer ) {\n\n var n = v.length,\n units = allocTexUnits( renderer, n );\n\n gl.uniform1iv( this.addr, units );\n\n for ( var i = 0; i !== n; ++ i ) {\n\n renderer.setTexture2D( v[ i ] || emptyTexture, units[ i ] );\n\n }\n\n }\n\n function setValueT6a( gl, v, renderer ) {\n\n var n = v.length,\n units = allocTexUnits( renderer, n );\n\n gl.uniform1iv( this.addr, units );\n\n for ( var i = 0; i !== n; ++ i ) {\n\n renderer.setTextureCube( v[ i ] || emptyCubeTexture, units[ i ] );\n\n }\n\n }\n\n // Helper to pick the right setter for a pure (bottom-level) array\n\n function getPureArraySetter( type ) {\n\n switch ( type ) {\n\n case 0x1406: return setValue1fv; // FLOAT\n case 0x8b50: return setValueV2a; // _VEC2\n case 0x8b51: return setValueV3a; // _VEC3\n case 0x8b52: return setValueV4a; // _VEC4\n\n case 0x8b5a: return setValueM2a; // _MAT2\n case 0x8b5b: return setValueM3a; // _MAT3\n case 0x8b5c: return setValueM4a; // _MAT4\n\n case 0x8b5e: return setValueT1a; // SAMPLER_2D\n case 0x8b60: return setValueT6a; // SAMPLER_CUBE\n\n case 0x1404: case 0x8b56: return setValue1iv; // INT, BOOL\n case 0x8b53: case 0x8b57: return setValue2iv; // _VEC2\n case 0x8b54: case 0x8b58: return setValue3iv; // _VEC3\n case 0x8b55: case 0x8b59: return setValue4iv; // _VEC4\n\n }\n\n }\n\n // --- Uniform Classes ---\n\n function SingleUniform( id, activeInfo, addr ) {\n\n this.id = id;\n this.addr = addr;\n this.setValue = getSingularSetter( activeInfo.type );\n\n // this.path = activeInfo.name; // DEBUG\n\n }\n\n function PureArrayUniform( id, activeInfo, addr ) {\n\n this.id = id;\n this.addr = addr;\n this.size = activeInfo.size;\n this.setValue = getPureArraySetter( activeInfo.type );\n\n // this.path = activeInfo.name; // DEBUG\n\n }\n\n function StructuredUniform( id ) {\n\n this.id = id;\n\n UniformContainer.call( this ); // mix-in\n\n }\n\n StructuredUniform.prototype.setValue = function ( gl, value ) {\n\n // Note: Don't need an extra 'renderer' parameter, since samplers\n // are not allowed in structured uniforms.\n\n var seq = this.seq;\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ];\n u.setValue( gl, value[ u.id ] );\n\n }\n\n };\n\n // --- Top-level ---\n\n // Parser - builds up the property tree from the path strings\n\n var RePathPart = /([\\w\\d_]+)(\\])?(\\[|\\.)?/g;\n\n // extracts\n // - the identifier (member name or array index)\n // - followed by an optional right bracket (found when array index)\n // - followed by an optional left bracket or dot (type of subscript)\n //\n // Note: These portions can be read in a non-overlapping fashion and\n // allow straightforward parsing of the hierarchy that WebGL encodes\n // in the uniform names.\n\n function addUniform( container, uniformObject ) {\n\n container.seq.push( uniformObject );\n container.map[ uniformObject.id ] = uniformObject;\n\n }\n\n function parseUniform( activeInfo, addr, container ) {\n\n var path = activeInfo.name,\n pathLength = path.length;\n\n // reset RegExp object, because of the early exit of a previous run\n RePathPart.lastIndex = 0;\n\n for ( ; ; ) {\n\n var match = RePathPart.exec( path ),\n matchEnd = RePathPart.lastIndex,\n\n id = match[ 1 ],\n idIsIndex = match[ 2 ] === ']',\n subscript = match[ 3 ];\n\n if ( idIsIndex ) id = id | 0; // convert to integer\n\n if ( subscript === undefined || subscript === '[' && matchEnd + 2 === pathLength ) {\n\n // bare name or \"pure\" bottom-level array \"[0]\" suffix\n\n addUniform( container, subscript === undefined ?\n new SingleUniform( id, activeInfo, addr ) :\n new PureArrayUniform( id, activeInfo, addr ) );\n\n break;\n\n } else {\n\n // step into inner node / create it in case it doesn't exist\n\n var map = container.map, next = map[ id ];\n\n if ( next === undefined ) {\n\n next = new StructuredUniform( id );\n addUniform( container, next );\n\n }\n\n container = next;\n\n }\n\n }\n\n }\n\n // Root Container\n\n function WebGLUniforms( gl, program, renderer ) {\n\n UniformContainer.call( this );\n\n this.renderer = renderer;\n\n var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );\n\n for ( var i = 0; i < n; ++ i ) {\n\n var info = gl.getActiveUniform( program, i ),\n path = info.name,\n addr = gl.getUniformLocation( program, path );\n\n parseUniform( info, addr, this );\n\n }\n\n }\n\n WebGLUniforms.prototype.setValue = function ( gl, name, value ) {\n\n var u = this.map[ name ];\n\n if ( u !== undefined ) u.setValue( gl, value, this.renderer );\n\n };\n\n WebGLUniforms.prototype.setOptional = function ( gl, object, name ) {\n\n var v = object[ name ];\n\n if ( v !== undefined ) this.setValue( gl, name, v );\n\n };\n\n\n // Static interface\n\n WebGLUniforms.upload = function ( gl, seq, values, renderer ) {\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ],\n v = values[ u.id ];\n\n if ( v.needsUpdate !== false ) {\n\n // note: always updating when .needsUpdate is undefined\n u.setValue( gl, v.value, renderer );\n\n }\n\n }\n\n };\n\n WebGLUniforms.seqWithValue = function ( seq, values ) {\n\n var r = [];\n\n for ( var i = 0, n = seq.length; i !== n; ++ i ) {\n\n var u = seq[ i ];\n if ( u.id in values ) r.push( u );\n\n }\n\n return r;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function addLineNumbers( string ) {\n\n var lines = string.split( '\\n' );\n\n for ( var i = 0; i < lines.length; i ++ ) {\n\n lines[ i ] = ( i + 1 ) + ': ' + lines[ i ];\n\n }\n\n return lines.join( '\\n' );\n\n }\n\n function WebGLShader( gl, type, string ) {\n\n var shader = gl.createShader( type );\n\n gl.shaderSource( shader, string );\n gl.compileShader( shader );\n\n if ( gl.getShaderParameter( shader, gl.COMPILE_STATUS ) === false ) {\n\n console.error( 'THREE.WebGLShader: Shader couldn\\'t compile.' );\n\n }\n\n if ( gl.getShaderInfoLog( shader ) !== '' ) {\n\n console.warn( 'THREE.WebGLShader: gl.getShaderInfoLog()', type === gl.VERTEX_SHADER ? 'vertex' : 'fragment', gl.getShaderInfoLog( shader ), addLineNumbers( string ) );\n\n }\n\n // --enable-privileged-webgl-extension\n // console.log( type, gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( shader ) );\n\n return shader;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var programIdCount = 0;\n\n function getEncodingComponents( encoding ) {\n\n switch ( encoding ) {\n\n case LinearEncoding:\n return [ 'Linear', '( value )' ];\n case sRGBEncoding:\n return [ 'sRGB', '( value )' ];\n case RGBEEncoding:\n return [ 'RGBE', '( value )' ];\n case RGBM7Encoding:\n return [ 'RGBM', '( value, 7.0 )' ];\n case RGBM16Encoding:\n return [ 'RGBM', '( value, 16.0 )' ];\n case RGBDEncoding:\n return [ 'RGBD', '( value, 256.0 )' ];\n case GammaEncoding:\n return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];\n default:\n throw new Error( 'unsupported encoding: ' + encoding );\n\n }\n\n }\n\n function getTexelDecodingFunction( functionName, encoding ) {\n\n var components = getEncodingComponents( encoding );\n return 'vec4 ' + functionName + '( vec4 value ) { return ' + components[ 0 ] + 'ToLinear' + components[ 1 ] + '; }';\n\n }\n\n function getTexelEncodingFunction( functionName, encoding ) {\n\n var components = getEncodingComponents( encoding );\n return 'vec4 ' + functionName + '( vec4 value ) { return LinearTo' + components[ 0 ] + components[ 1 ] + '; }';\n\n }\n\n function getToneMappingFunction( functionName, toneMapping ) {\n\n var toneMappingName;\n\n switch ( toneMapping ) {\n\n case LinearToneMapping:\n toneMappingName = 'Linear';\n break;\n\n case ReinhardToneMapping:\n toneMappingName = 'Reinhard';\n break;\n\n case Uncharted2ToneMapping:\n toneMappingName = 'Uncharted2';\n break;\n\n case CineonToneMapping:\n toneMappingName = 'OptimizedCineon';\n break;\n\n default:\n throw new Error( 'unsupported toneMapping: ' + toneMapping );\n\n }\n\n return 'vec3 ' + functionName + '( vec3 color ) { return ' + toneMappingName + 'ToneMapping( color ); }';\n\n }\n\n function generateExtensions( extensions, parameters, rendererExtensions ) {\n\n extensions = extensions || {};\n\n var chunks = [\n ( extensions.derivatives || parameters.envMapCubeUV || parameters.bumpMap || parameters.normalMap || parameters.flatShading ) ? '#extension GL_OES_standard_derivatives : enable' : '',\n ( extensions.fragDepth || parameters.logarithmicDepthBuffer ) && rendererExtensions.get( 'EXT_frag_depth' ) ? '#extension GL_EXT_frag_depth : enable' : '',\n ( extensions.drawBuffers ) && rendererExtensions.get( 'WEBGL_draw_buffers' ) ? '#extension GL_EXT_draw_buffers : require' : '',\n ( extensions.shaderTextureLOD || parameters.envMap ) && rendererExtensions.get( 'EXT_shader_texture_lod' ) ? '#extension GL_EXT_shader_texture_lod : enable' : ''\n ];\n\n return chunks.filter( filterEmptyLine ).join( '\\n' );\n\n }\n\n function generateDefines( defines ) {\n\n var chunks = [];\n\n for ( var name in defines ) {\n\n var value = defines[ name ];\n\n if ( value === false ) continue;\n\n chunks.push( '#define ' + name + ' ' + value );\n\n }\n\n return chunks.join( '\\n' );\n\n }\n\n function fetchAttributeLocations( gl, program ) {\n\n var attributes = {};\n\n var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );\n\n for ( var i = 0; i < n; i ++ ) {\n\n var info = gl.getActiveAttrib( program, i );\n var name = info.name;\n\n // console.log( 'THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:', name, i );\n\n attributes[ name ] = gl.getAttribLocation( program, name );\n\n }\n\n return attributes;\n\n }\n\n function filterEmptyLine( string ) {\n\n return string !== '';\n\n }\n\n function replaceLightNums( string, parameters ) {\n\n return string\n .replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights )\n .replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights )\n .replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights )\n .replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights )\n .replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights );\n\n }\n\n function replaceClippingPlaneNums( string, parameters ) {\n\n return string\n .replace( /NUM_CLIPPING_PLANES/g, parameters.numClippingPlanes )\n .replace( /UNION_CLIPPING_PLANES/g, ( parameters.numClippingPlanes - parameters.numClipIntersection ) );\n\n }\n\n function parseIncludes( string ) {\n\n var pattern = /^[ \\t]*#include +<([\\w\\d.]+)>/gm;\n\n function replace( match, include ) {\n\n var replace = ShaderChunk[ include ];\n\n if ( replace === undefined ) {\n\n throw new Error( 'Can not resolve #include <' + include + '>' );\n\n }\n\n return parseIncludes( replace );\n\n }\n\n return string.replace( pattern, replace );\n\n }\n\n function unrollLoops( string ) {\n\n var pattern = /#pragma unroll_loop[\\s]+?for \\( int i \\= (\\d+)\\; i < (\\d+)\\; i \\+\\+ \\) \\{([\\s\\S]+?)(?=\\})\\}/g;\n\n function replace( match, start, end, snippet ) {\n\n var unroll = '';\n\n for ( var i = parseInt( start ); i < parseInt( end ); i ++ ) {\n\n unroll += snippet.replace( /\\[ i \\]/g, '[ ' + i + ' ]' );\n\n }\n\n return unroll;\n\n }\n\n return string.replace( pattern, replace );\n\n }\n\n function WebGLProgram( renderer, extensions, code, material, shader, parameters ) {\n\n var gl = renderer.context;\n\n var defines = material.defines;\n\n var vertexShader = shader.vertexShader;\n var fragmentShader = shader.fragmentShader;\n\n var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';\n\n if ( parameters.shadowMapType === PCFShadowMap ) {\n\n shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';\n\n } else if ( parameters.shadowMapType === PCFSoftShadowMap ) {\n\n shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';\n\n }\n\n var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';\n var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n\n if ( parameters.envMap ) {\n\n switch ( material.envMap.mapping ) {\n\n case CubeReflectionMapping:\n case CubeRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_CUBE';\n break;\n\n case CubeUVReflectionMapping:\n case CubeUVRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_CUBE_UV';\n break;\n\n case EquirectangularReflectionMapping:\n case EquirectangularRefractionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';\n break;\n\n case SphericalReflectionMapping:\n envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';\n break;\n\n }\n\n switch ( material.envMap.mapping ) {\n\n case CubeRefractionMapping:\n case EquirectangularRefractionMapping:\n envMapModeDefine = 'ENVMAP_MODE_REFRACTION';\n break;\n\n }\n\n switch ( material.combine ) {\n\n case MultiplyOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';\n break;\n\n case MixOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';\n break;\n\n case AddOperation:\n envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';\n break;\n\n }\n\n }\n\n var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;\n\n // console.log( 'building new program ' );\n\n //\n\n var customExtensions = generateExtensions( material.extensions, parameters, extensions );\n\n var customDefines = generateDefines( defines );\n\n //\n\n var program = gl.createProgram();\n\n var prefixVertex, prefixFragment;\n\n if ( material.isRawShaderMaterial ) {\n\n prefixVertex = [\n\n customDefines\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n if ( prefixVertex.length > 0 ) {\n\n prefixVertex += '\\n';\n\n }\n\n prefixFragment = [\n\n customExtensions,\n customDefines\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n if ( prefixFragment.length > 0 ) {\n\n prefixFragment += '\\n';\n\n }\n\n } else {\n\n prefixVertex = [\n\n 'precision ' + parameters.precision + ' float;',\n 'precision ' + parameters.precision + ' int;',\n\n '#define SHADER_NAME ' + shader.name,\n\n customDefines,\n\n parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',\n\n '#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n '#define MAX_BONES ' + parameters.maxBones,\n ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',\n ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',\n\n parameters.map ? '#define USE_MAP' : '',\n parameters.envMap ? '#define USE_ENVMAP' : '',\n parameters.envMap ? '#define ' + envMapModeDefine : '',\n parameters.lightMap ? '#define USE_LIGHTMAP' : '',\n parameters.aoMap ? '#define USE_AOMAP' : '',\n parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n parameters.bumpMap ? '#define USE_BUMPMAP' : '',\n parameters.normalMap ? '#define USE_NORMALMAP' : '',\n parameters.displacementMap && parameters.supportsVertexTextures ? '#define USE_DISPLACEMENTMAP' : '',\n parameters.specularMap ? '#define USE_SPECULARMAP' : '',\n parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n parameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n parameters.vertexColors ? '#define USE_COLOR' : '',\n\n parameters.flatShading ? '#define FLAT_SHADED' : '',\n\n parameters.skinning ? '#define USE_SKINNING' : '',\n parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',\n\n parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',\n parameters.morphNormals && parameters.flatShading === false ? '#define USE_MORPHNORMALS' : '',\n parameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n parameters.flipSided ? '#define FLIP_SIDED' : '',\n\n parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',\n\n parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n 'uniform mat4 modelMatrix;',\n 'uniform mat4 modelViewMatrix;',\n 'uniform mat4 projectionMatrix;',\n 'uniform mat4 viewMatrix;',\n 'uniform mat3 normalMatrix;',\n 'uniform vec3 cameraPosition;',\n\n 'attribute vec3 position;',\n 'attribute vec3 normal;',\n 'attribute vec2 uv;',\n\n '#ifdef USE_COLOR',\n\n ' attribute vec3 color;',\n\n '#endif',\n\n '#ifdef USE_MORPHTARGETS',\n\n ' attribute vec3 morphTarget0;',\n ' attribute vec3 morphTarget1;',\n ' attribute vec3 morphTarget2;',\n ' attribute vec3 morphTarget3;',\n\n ' #ifdef USE_MORPHNORMALS',\n\n ' attribute vec3 morphNormal0;',\n ' attribute vec3 morphNormal1;',\n ' attribute vec3 morphNormal2;',\n ' attribute vec3 morphNormal3;',\n\n ' #else',\n\n ' attribute vec3 morphTarget4;',\n ' attribute vec3 morphTarget5;',\n ' attribute vec3 morphTarget6;',\n ' attribute vec3 morphTarget7;',\n\n ' #endif',\n\n '#endif',\n\n '#ifdef USE_SKINNING',\n\n ' attribute vec4 skinIndex;',\n ' attribute vec4 skinWeight;',\n\n '#endif',\n\n '\\n'\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n prefixFragment = [\n\n customExtensions,\n\n 'precision ' + parameters.precision + ' float;',\n 'precision ' + parameters.precision + ' int;',\n\n '#define SHADER_NAME ' + shader.name,\n\n customDefines,\n\n parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',\n\n '#define GAMMA_FACTOR ' + gammaFactorDefine,\n\n ( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',\n ( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',\n\n parameters.map ? '#define USE_MAP' : '',\n parameters.envMap ? '#define USE_ENVMAP' : '',\n parameters.envMap ? '#define ' + envMapTypeDefine : '',\n parameters.envMap ? '#define ' + envMapModeDefine : '',\n parameters.envMap ? '#define ' + envMapBlendingDefine : '',\n parameters.lightMap ? '#define USE_LIGHTMAP' : '',\n parameters.aoMap ? '#define USE_AOMAP' : '',\n parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',\n parameters.bumpMap ? '#define USE_BUMPMAP' : '',\n parameters.normalMap ? '#define USE_NORMALMAP' : '',\n parameters.specularMap ? '#define USE_SPECULARMAP' : '',\n parameters.roughnessMap ? '#define USE_ROUGHNESSMAP' : '',\n parameters.metalnessMap ? '#define USE_METALNESSMAP' : '',\n parameters.alphaMap ? '#define USE_ALPHAMAP' : '',\n parameters.vertexColors ? '#define USE_COLOR' : '',\n\n parameters.gradientMap ? '#define USE_GRADIENTMAP' : '',\n\n parameters.flatShading ? '#define FLAT_SHADED' : '',\n\n parameters.doubleSided ? '#define DOUBLE_SIDED' : '',\n parameters.flipSided ? '#define FLIP_SIDED' : '',\n\n parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',\n parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',\n\n parameters.premultipliedAlpha ? '#define PREMULTIPLIED_ALPHA' : '',\n\n parameters.physicallyCorrectLights ? '#define PHYSICALLY_CORRECT_LIGHTS' : '',\n\n parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',\n parameters.logarithmicDepthBuffer && extensions.get( 'EXT_frag_depth' ) ? '#define USE_LOGDEPTHBUF_EXT' : '',\n\n parameters.envMap && extensions.get( 'EXT_shader_texture_lod' ) ? '#define TEXTURE_LOD_EXT' : '',\n\n 'uniform mat4 viewMatrix;',\n 'uniform vec3 cameraPosition;',\n\n ( parameters.toneMapping !== NoToneMapping ) ? '#define TONE_MAPPING' : '',\n ( parameters.toneMapping !== NoToneMapping ) ? ShaderChunk[ 'tonemapping_pars_fragment' ] : '', // this code is required here because it is used by the toneMapping() function defined below\n ( parameters.toneMapping !== NoToneMapping ) ? getToneMappingFunction( 'toneMapping', parameters.toneMapping ) : '',\n\n parameters.dithering ? '#define DITHERING' : '',\n\n ( parameters.outputEncoding || parameters.mapEncoding || parameters.envMapEncoding || parameters.emissiveMapEncoding ) ? ShaderChunk[ 'encodings_pars_fragment' ] : '', // this code is required here because it is used by the various encoding/decoding function defined below\n parameters.mapEncoding ? getTexelDecodingFunction( 'mapTexelToLinear', parameters.mapEncoding ) : '',\n parameters.envMapEncoding ? getTexelDecodingFunction( 'envMapTexelToLinear', parameters.envMapEncoding ) : '',\n parameters.emissiveMapEncoding ? getTexelDecodingFunction( 'emissiveMapTexelToLinear', parameters.emissiveMapEncoding ) : '',\n parameters.outputEncoding ? getTexelEncodingFunction( 'linearToOutputTexel', parameters.outputEncoding ) : '',\n\n parameters.depthPacking ? '#define DEPTH_PACKING ' + material.depthPacking : '',\n\n '\\n'\n\n ].filter( filterEmptyLine ).join( '\\n' );\n\n }\n\n vertexShader = parseIncludes( vertexShader );\n vertexShader = replaceLightNums( vertexShader, parameters );\n vertexShader = replaceClippingPlaneNums( vertexShader, parameters );\n\n fragmentShader = parseIncludes( fragmentShader );\n fragmentShader = replaceLightNums( fragmentShader, parameters );\n fragmentShader = replaceClippingPlaneNums( fragmentShader, parameters );\n\n vertexShader = unrollLoops( vertexShader );\n fragmentShader = unrollLoops( fragmentShader );\n\n var vertexGlsl = prefixVertex + vertexShader;\n var fragmentGlsl = prefixFragment + fragmentShader;\n\n // console.log( '*VERTEX*', vertexGlsl );\n // console.log( '*FRAGMENT*', fragmentGlsl );\n\n var glVertexShader = WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );\n var glFragmentShader = WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );\n\n gl.attachShader( program, glVertexShader );\n gl.attachShader( program, glFragmentShader );\n\n // Force a particular attribute to index 0.\n\n if ( material.index0AttributeName !== undefined ) {\n\n gl.bindAttribLocation( program, 0, material.index0AttributeName );\n\n } else if ( parameters.morphTargets === true ) {\n\n // programs with morphTargets displace position out of attribute 0\n gl.bindAttribLocation( program, 0, 'position' );\n\n }\n\n gl.linkProgram( program );\n\n var programLog = gl.getProgramInfoLog( program ).trim();\n var vertexLog = gl.getShaderInfoLog( glVertexShader ).trim();\n var fragmentLog = gl.getShaderInfoLog( glFragmentShader ).trim();\n\n var runnable = true;\n var haveDiagnostics = true;\n\n // console.log( '**VERTEX**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glVertexShader ) );\n // console.log( '**FRAGMENT**', gl.getExtension( 'WEBGL_debug_shaders' ).getTranslatedShaderSource( glFragmentShader ) );\n\n if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {\n\n runnable = false;\n\n console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLog, vertexLog, fragmentLog );\n\n } else if ( programLog !== '' ) {\n\n console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLog );\n\n } else if ( vertexLog === '' || fragmentLog === '' ) {\n\n haveDiagnostics = false;\n\n }\n\n if ( haveDiagnostics ) {\n\n this.diagnostics = {\n\n runnable: runnable,\n material: material,\n\n programLog: programLog,\n\n vertexShader: {\n\n log: vertexLog,\n prefix: prefixVertex\n\n },\n\n fragmentShader: {\n\n log: fragmentLog,\n prefix: prefixFragment\n\n }\n\n };\n\n }\n\n // clean up\n\n gl.deleteShader( glVertexShader );\n gl.deleteShader( glFragmentShader );\n\n // set up caching for uniform locations\n\n var cachedUniforms;\n\n this.getUniforms = function () {\n\n if ( cachedUniforms === undefined ) {\n\n cachedUniforms = new WebGLUniforms( gl, program, renderer );\n\n }\n\n return cachedUniforms;\n\n };\n\n // set up caching for attribute locations\n\n var cachedAttributes;\n\n this.getAttributes = function () {\n\n if ( cachedAttributes === undefined ) {\n\n cachedAttributes = fetchAttributeLocations( gl, program );\n\n }\n\n return cachedAttributes;\n\n };\n\n // free resource\n\n this.destroy = function () {\n\n gl.deleteProgram( program );\n this.program = undefined;\n\n };\n\n // DEPRECATED\n\n Object.defineProperties( this, {\n\n uniforms: {\n get: function () {\n\n console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );\n return this.getUniforms();\n\n }\n },\n\n attributes: {\n get: function () {\n\n console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );\n return this.getAttributes();\n\n }\n }\n\n } );\n\n\n //\n\n this.id = programIdCount ++;\n this.code = code;\n this.usedTimes = 1;\n this.program = program;\n this.vertexShader = glVertexShader;\n this.fragmentShader = glFragmentShader;\n\n return this;\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLPrograms( renderer, extensions, capabilities ) {\n\n var programs = [];\n\n var shaderIDs = {\n MeshDepthMaterial: 'depth',\n MeshDistanceMaterial: 'distanceRGBA',\n MeshNormalMaterial: 'normal',\n MeshBasicMaterial: 'basic',\n MeshLambertMaterial: 'lambert',\n MeshPhongMaterial: 'phong',\n MeshToonMaterial: 'phong',\n MeshStandardMaterial: 'physical',\n MeshPhysicalMaterial: 'physical',\n LineBasicMaterial: 'basic',\n LineDashedMaterial: 'dashed',\n PointsMaterial: 'points',\n ShadowMaterial: 'shadow'\n };\n\n var parameterNames = [\n \"precision\", \"supportsVertexTextures\", \"map\", \"mapEncoding\", \"envMap\", \"envMapMode\", \"envMapEncoding\",\n \"lightMap\", \"aoMap\", \"emissiveMap\", \"emissiveMapEncoding\", \"bumpMap\", \"normalMap\", \"displacementMap\", \"specularMap\",\n \"roughnessMap\", \"metalnessMap\", \"gradientMap\",\n \"alphaMap\", \"combine\", \"vertexColors\", \"fog\", \"useFog\", \"fogExp\",\n \"flatShading\", \"sizeAttenuation\", \"logarithmicDepthBuffer\", \"skinning\",\n \"maxBones\", \"useVertexTexture\", \"morphTargets\", \"morphNormals\",\n \"maxMorphTargets\", \"maxMorphNormals\", \"premultipliedAlpha\",\n \"numDirLights\", \"numPointLights\", \"numSpotLights\", \"numHemiLights\", \"numRectAreaLights\",\n \"shadowMapEnabled\", \"shadowMapType\", \"toneMapping\", 'physicallyCorrectLights',\n \"alphaTest\", \"doubleSided\", \"flipSided\", \"numClippingPlanes\", \"numClipIntersection\", \"depthPacking\", \"dithering\"\n ];\n\n\n function allocateBones( object ) {\n\n var skeleton = object.skeleton;\n var bones = skeleton.bones;\n\n if ( capabilities.floatVertexTextures ) {\n\n return 1024;\n\n } else {\n\n // default for when object is not specified\n // ( for example when prebuilding shader to be used with multiple objects )\n //\n // - leave some extra space for other uniforms\n // - limit here is ANGLE's 254 max uniform vectors\n // (up to 54 should be safe)\n\n var nVertexUniforms = capabilities.maxVertexUniforms;\n var nVertexMatrices = Math.floor( ( nVertexUniforms - 20 ) / 4 );\n\n var maxBones = Math.min( nVertexMatrices, bones.length );\n\n if ( maxBones < bones.length ) {\n\n console.warn( 'THREE.WebGLRenderer: Skeleton has ' + bones.length + ' bones. This GPU supports ' + maxBones + '.' );\n return 0;\n\n }\n\n return maxBones;\n\n }\n\n }\n\n function getTextureEncodingFromMap( map, gammaOverrideLinear ) {\n\n var encoding;\n\n if ( ! map ) {\n\n encoding = LinearEncoding;\n\n } else if ( map.isTexture ) {\n\n encoding = map.encoding;\n\n } else if ( map.isWebGLRenderTarget ) {\n\n console.warn( \"THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead.\" );\n encoding = map.texture.encoding;\n\n }\n\n // add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point.\n if ( encoding === LinearEncoding && gammaOverrideLinear ) {\n\n encoding = GammaEncoding;\n\n }\n\n return encoding;\n\n }\n\n this.getParameters = function ( material, lights, shadows, fog, nClipPlanes, nClipIntersection, object ) {\n\n var shaderID = shaderIDs[ material.type ];\n\n // heuristics to create shader parameters according to lights in the scene\n // (not to blow over maxLights budget)\n\n var maxBones = object.isSkinnedMesh ? allocateBones( object ) : 0;\n var precision = capabilities.precision;\n\n if ( material.precision !== null ) {\n\n precision = capabilities.getMaxPrecision( material.precision );\n\n if ( precision !== material.precision ) {\n\n console.warn( 'THREE.WebGLProgram.getParameters:', material.precision, 'not supported, using', precision, 'instead.' );\n\n }\n\n }\n\n var currentRenderTarget = renderer.getRenderTarget();\n\n var parameters = {\n\n shaderID: shaderID,\n\n precision: precision,\n supportsVertexTextures: capabilities.vertexTextures,\n outputEncoding: getTextureEncodingFromMap( ( ! currentRenderTarget ) ? null : currentRenderTarget.texture, renderer.gammaOutput ),\n map: !! material.map,\n mapEncoding: getTextureEncodingFromMap( material.map, renderer.gammaInput ),\n envMap: !! material.envMap,\n envMapMode: material.envMap && material.envMap.mapping,\n envMapEncoding: getTextureEncodingFromMap( material.envMap, renderer.gammaInput ),\n envMapCubeUV: ( !! material.envMap ) && ( ( material.envMap.mapping === CubeUVReflectionMapping ) || ( material.envMap.mapping === CubeUVRefractionMapping ) ),\n lightMap: !! material.lightMap,\n aoMap: !! material.aoMap,\n emissiveMap: !! material.emissiveMap,\n emissiveMapEncoding: getTextureEncodingFromMap( material.emissiveMap, renderer.gammaInput ),\n bumpMap: !! material.bumpMap,\n normalMap: !! material.normalMap,\n displacementMap: !! material.displacementMap,\n roughnessMap: !! material.roughnessMap,\n metalnessMap: !! material.metalnessMap,\n specularMap: !! material.specularMap,\n alphaMap: !! material.alphaMap,\n\n gradientMap: !! material.gradientMap,\n\n combine: material.combine,\n\n vertexColors: material.vertexColors,\n\n fog: !! fog,\n useFog: material.fog,\n fogExp: ( fog && fog.isFogExp2 ),\n\n flatShading: material.flatShading,\n\n sizeAttenuation: material.sizeAttenuation,\n logarithmicDepthBuffer: capabilities.logarithmicDepthBuffer,\n\n skinning: material.skinning && maxBones > 0,\n maxBones: maxBones,\n useVertexTexture: capabilities.floatVertexTextures,\n\n morphTargets: material.morphTargets,\n morphNormals: material.morphNormals,\n maxMorphTargets: renderer.maxMorphTargets,\n maxMorphNormals: renderer.maxMorphNormals,\n\n numDirLights: lights.directional.length,\n numPointLights: lights.point.length,\n numSpotLights: lights.spot.length,\n numRectAreaLights: lights.rectArea.length,\n numHemiLights: lights.hemi.length,\n\n numClippingPlanes: nClipPlanes,\n numClipIntersection: nClipIntersection,\n\n dithering: material.dithering,\n\n shadowMapEnabled: renderer.shadowMap.enabled && object.receiveShadow && shadows.length > 0,\n shadowMapType: renderer.shadowMap.type,\n\n toneMapping: renderer.toneMapping,\n physicallyCorrectLights: renderer.physicallyCorrectLights,\n\n premultipliedAlpha: material.premultipliedAlpha,\n\n alphaTest: material.alphaTest,\n doubleSided: material.side === DoubleSide,\n flipSided: material.side === BackSide,\n\n depthPacking: ( material.depthPacking !== undefined ) ? material.depthPacking : false\n\n };\n\n return parameters;\n\n };\n\n this.getProgramCode = function ( material, parameters ) {\n\n var array = [];\n\n if ( parameters.shaderID ) {\n\n array.push( parameters.shaderID );\n\n } else {\n\n array.push( material.fragmentShader );\n array.push( material.vertexShader );\n\n }\n\n if ( material.defines !== undefined ) {\n\n for ( var name in material.defines ) {\n\n array.push( name );\n array.push( material.defines[ name ] );\n\n }\n\n }\n\n for ( var i = 0; i < parameterNames.length; i ++ ) {\n\n array.push( parameters[ parameterNames[ i ] ] );\n\n }\n\n array.push( material.onBeforeCompile.toString() );\n\n array.push( renderer.gammaOutput );\n\n return array.join();\n\n };\n\n this.acquireProgram = function ( material, shader, parameters, code ) {\n\n var program;\n\n // Check if code has been already compiled\n for ( var p = 0, pl = programs.length; p < pl; p ++ ) {\n\n var programInfo = programs[ p ];\n\n if ( programInfo.code === code ) {\n\n program = programInfo;\n ++ program.usedTimes;\n\n break;\n\n }\n\n }\n\n if ( program === undefined ) {\n\n program = new WebGLProgram( renderer, extensions, code, material, shader, parameters );\n programs.push( program );\n\n }\n\n return program;\n\n };\n\n this.releaseProgram = function ( program ) {\n\n if ( -- program.usedTimes === 0 ) {\n\n // Remove from unordered set\n var i = programs.indexOf( program );\n programs[ i ] = programs[ programs.length - 1 ];\n programs.pop();\n\n // Free WebGL resources\n program.destroy();\n\n }\n\n };\n\n // Exposed for resource monitoring & error feedback via renderer.info:\n this.programs = programs;\n\n }\n\n /**\n * @author fordacious / fordacious.github.io\n */\n\n function WebGLProperties() {\n\n var properties = new WeakMap();\n\n function get( object ) {\n\n var map = properties.get( object );\n\n if ( map === undefined ) {\n\n map = {};\n properties.set( object, map );\n\n }\n\n return map;\n\n }\n\n function remove( object ) {\n\n properties.delete( object );\n\n }\n\n function update( object, key, value ) {\n\n properties.get( object )[ key ] = value;\n\n }\n\n function dispose() {\n\n properties = new WeakMap();\n\n }\n\n return {\n get: get,\n remove: remove,\n update: update,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function painterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } else if ( a.program && b.program && a.program !== b.program ) {\n\n return a.program.id - b.program.id;\n\n } else if ( a.material.id !== b.material.id ) {\n\n return a.material.id - b.material.id;\n\n } else if ( a.z !== b.z ) {\n\n return a.z - b.z;\n\n } else {\n\n return a.id - b.id;\n\n }\n\n }\n\n function reversePainterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } if ( a.z !== b.z ) {\n\n return b.z - a.z;\n\n } else {\n\n return a.id - b.id;\n\n }\n\n }\n\n function WebGLRenderList() {\n\n var renderItems = [];\n var renderItemsIndex = 0;\n\n var opaque = [];\n var transparent = [];\n\n function init() {\n\n renderItemsIndex = 0;\n\n opaque.length = 0;\n transparent.length = 0;\n\n }\n\n function push( object, geometry, material, z, group ) {\n\n var renderItem = renderItems[ renderItemsIndex ];\n\n if ( renderItem === undefined ) {\n\n renderItem = {\n id: object.id,\n object: object,\n geometry: geometry,\n material: material,\n program: material.program,\n renderOrder: object.renderOrder,\n z: z,\n group: group\n };\n\n renderItems[ renderItemsIndex ] = renderItem;\n\n } else {\n\n renderItem.id = object.id;\n renderItem.object = object;\n renderItem.geometry = geometry;\n renderItem.material = material;\n renderItem.program = material.program;\n renderItem.renderOrder = object.renderOrder;\n renderItem.z = z;\n renderItem.group = group;\n\n }\n\n ( material.transparent === true ? transparent : opaque ).push( renderItem );\n\n renderItemsIndex ++;\n\n }\n\n function sort() {\n\n if ( opaque.length > 1 ) opaque.sort( painterSortStable );\n if ( transparent.length > 1 ) transparent.sort( reversePainterSortStable );\n\n }\n\n return {\n opaque: opaque,\n transparent: transparent,\n\n init: init,\n push: push,\n\n sort: sort\n };\n\n }\n\n function WebGLRenderLists() {\n\n var lists = {};\n\n function get( scene, camera ) {\n\n var hash = scene.id + ',' + camera.id;\n var list = lists[ hash ];\n\n if ( list === undefined ) {\n\n // console.log( 'THREE.WebGLRenderLists:', hash );\n\n list = new WebGLRenderList();\n lists[ hash ] = list;\n\n }\n\n return list;\n\n }\n\n function dispose() {\n\n lists = {};\n\n }\n\n return {\n get: get,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function UniformsCache() {\n\n var lights = {};\n\n return {\n\n get: function ( light ) {\n\n if ( lights[ light.id ] !== undefined ) {\n\n return lights[ light.id ];\n\n }\n\n var uniforms;\n\n switch ( light.type ) {\n\n case 'DirectionalLight':\n uniforms = {\n direction: new Vector3(),\n color: new Color(),\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2()\n };\n break;\n\n case 'SpotLight':\n uniforms = {\n position: new Vector3(),\n direction: new Vector3(),\n color: new Color(),\n distance: 0,\n coneCos: 0,\n penumbraCos: 0,\n decay: 0,\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2()\n };\n break;\n\n case 'PointLight':\n uniforms = {\n position: new Vector3(),\n color: new Color(),\n distance: 0,\n decay: 0,\n\n shadow: false,\n shadowBias: 0,\n shadowRadius: 1,\n shadowMapSize: new Vector2(),\n shadowCameraNear: 1,\n shadowCameraFar: 1000\n };\n break;\n\n case 'HemisphereLight':\n uniforms = {\n direction: new Vector3(),\n skyColor: new Color(),\n groundColor: new Color()\n };\n break;\n\n case 'RectAreaLight':\n uniforms = {\n color: new Color(),\n position: new Vector3(),\n halfWidth: new Vector3(),\n halfHeight: new Vector3()\n // TODO (abelnation): set RectAreaLight shadow uniforms\n };\n break;\n\n }\n\n lights[ light.id ] = uniforms;\n\n return uniforms;\n\n }\n\n };\n\n }\n\n var count = 0;\n\n function WebGLLights() {\n\n var cache = new UniformsCache();\n\n var state = {\n\n id: count ++,\n\n hash: '',\n\n ambient: [ 0, 0, 0 ],\n directional: [],\n directionalShadowMap: [],\n directionalShadowMatrix: [],\n spot: [],\n spotShadowMap: [],\n spotShadowMatrix: [],\n rectArea: [],\n point: [],\n pointShadowMap: [],\n pointShadowMatrix: [],\n hemi: []\n\n };\n\n var vector3 = new Vector3();\n var matrix4 = new Matrix4();\n var matrix42 = new Matrix4();\n\n function setup( lights, shadows, camera ) {\n\n var r = 0, g = 0, b = 0;\n\n var directionalLength = 0;\n var pointLength = 0;\n var spotLength = 0;\n var rectAreaLength = 0;\n var hemiLength = 0;\n\n var viewMatrix = camera.matrixWorldInverse;\n\n for ( var i = 0, l = lights.length; i < l; i ++ ) {\n\n var light = lights[ i ];\n\n var color = light.color;\n var intensity = light.intensity;\n var distance = light.distance;\n\n var shadowMap = ( light.shadow && light.shadow.map ) ? light.shadow.map.texture : null;\n\n if ( light.isAmbientLight ) {\n\n r += color.r * intensity;\n g += color.g * intensity;\n b += color.b * intensity;\n\n } else if ( light.isDirectionalLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n vector3.setFromMatrixPosition( light.target.matrixWorld );\n uniforms.direction.sub( vector3 );\n uniforms.direction.transformDirection( viewMatrix );\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n\n }\n\n state.directionalShadowMap[ directionalLength ] = shadowMap;\n state.directionalShadowMatrix[ directionalLength ] = light.shadow.matrix;\n state.directional[ directionalLength ] = uniforms;\n\n directionalLength ++;\n\n } else if ( light.isSpotLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n uniforms.color.copy( color ).multiplyScalar( intensity );\n uniforms.distance = distance;\n\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n vector3.setFromMatrixPosition( light.target.matrixWorld );\n uniforms.direction.sub( vector3 );\n uniforms.direction.transformDirection( viewMatrix );\n\n uniforms.coneCos = Math.cos( light.angle );\n uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );\n uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n\n }\n\n state.spotShadowMap[ spotLength ] = shadowMap;\n state.spotShadowMatrix[ spotLength ] = light.shadow.matrix;\n state.spot[ spotLength ] = uniforms;\n\n spotLength ++;\n\n } else if ( light.isRectAreaLight ) {\n\n var uniforms = cache.get( light );\n\n // (a) intensity is the total visible light emitted\n //uniforms.color.copy( color ).multiplyScalar( intensity / ( light.width * light.height * Math.PI ) );\n\n // (b) intensity is the brightness of the light\n uniforms.color.copy( color ).multiplyScalar( intensity );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n // extract local rotation of light to derive width/height half vectors\n matrix42.identity();\n matrix4.copy( light.matrixWorld );\n matrix4.premultiply( viewMatrix );\n matrix42.extractRotation( matrix4 );\n\n uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );\n uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );\n\n uniforms.halfWidth.applyMatrix4( matrix42 );\n uniforms.halfHeight.applyMatrix4( matrix42 );\n\n // TODO (abelnation): RectAreaLight distance?\n // uniforms.distance = distance;\n\n state.rectArea[ rectAreaLength ] = uniforms;\n\n rectAreaLength ++;\n\n } else if ( light.isPointLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.position.setFromMatrixPosition( light.matrixWorld );\n uniforms.position.applyMatrix4( viewMatrix );\n\n uniforms.color.copy( light.color ).multiplyScalar( light.intensity );\n uniforms.distance = light.distance;\n uniforms.decay = ( light.distance === 0 ) ? 0.0 : light.decay;\n\n uniforms.shadow = light.castShadow;\n\n if ( light.castShadow ) {\n\n var shadow = light.shadow;\n\n uniforms.shadowBias = shadow.bias;\n uniforms.shadowRadius = shadow.radius;\n uniforms.shadowMapSize = shadow.mapSize;\n uniforms.shadowCameraNear = shadow.camera.near;\n uniforms.shadowCameraFar = shadow.camera.far;\n\n }\n\n state.pointShadowMap[ pointLength ] = shadowMap;\n state.pointShadowMatrix[ pointLength ] = light.shadow.matrix;\n state.point[ pointLength ] = uniforms;\n\n pointLength ++;\n\n } else if ( light.isHemisphereLight ) {\n\n var uniforms = cache.get( light );\n\n uniforms.direction.setFromMatrixPosition( light.matrixWorld );\n uniforms.direction.transformDirection( viewMatrix );\n uniforms.direction.normalize();\n\n uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );\n uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );\n\n state.hemi[ hemiLength ] = uniforms;\n\n hemiLength ++;\n\n }\n\n }\n\n state.ambient[ 0 ] = r;\n state.ambient[ 1 ] = g;\n state.ambient[ 2 ] = b;\n\n state.directional.length = directionalLength;\n state.spot.length = spotLength;\n state.rectArea.length = rectAreaLength;\n state.point.length = pointLength;\n state.hemi.length = hemiLength;\n\n state.hash = state.id + ',' + directionalLength + ',' + pointLength + ',' + spotLength + ',' + rectAreaLength + ',' + hemiLength + ',' + shadows.length;\n\n }\n\n return {\n setup: setup,\n state: state\n };\n\n }\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WebGLRenderState() {\n\n var lights = new WebGLLights();\n\n var lightsArray = [];\n var shadowsArray = [];\n var spritesArray = [];\n\n function init() {\n\n lightsArray.length = 0;\n shadowsArray.length = 0;\n spritesArray.length = 0;\n\n }\n\n function pushLight( light ) {\n\n lightsArray.push( light );\n\n }\n\n function pushShadow( shadowLight ) {\n\n shadowsArray.push( shadowLight );\n\n }\n\n function pushSprite( shadowLight ) {\n\n spritesArray.push( shadowLight );\n\n }\n\n function setupLights( camera ) {\n\n lights.setup( lightsArray, shadowsArray, camera );\n\n }\n\n var state = {\n lightsArray: lightsArray,\n shadowsArray: shadowsArray,\n spritesArray: spritesArray,\n\n lights: lights\n };\n\n return {\n init: init,\n state: state,\n setupLights: setupLights,\n\n pushLight: pushLight,\n pushShadow: pushShadow,\n pushSprite: pushSprite\n };\n\n }\n\n function WebGLRenderStates() {\n\n var renderStates = {};\n\n function get( scene, camera ) {\n\n var hash = scene.id + ',' + camera.id;\n\n var renderState = renderStates[ hash ];\n\n if ( renderState === undefined ) {\n\n renderState = new WebGLRenderState();\n renderStates[ hash ] = renderState;\n\n }\n\n return renderState;\n\n }\n\n function dispose() {\n\n renderStates = {};\n\n }\n\n return {\n get: get,\n dispose: dispose\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author bhouston / https://clara.io\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n *\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>\n * }\n */\n\n function MeshDepthMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshDepthMaterial';\n\n this.depthPacking = BasicDepthPacking;\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.map = null;\n\n this.alphaMap = null;\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshDepthMaterial.prototype = Object.create( Material.prototype );\n MeshDepthMaterial.prototype.constructor = MeshDepthMaterial;\n\n MeshDepthMaterial.prototype.isMeshDepthMaterial = true;\n\n MeshDepthMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.depthPacking = source.depthPacking;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n this.map = source.map;\n\n this.alphaMap = source.alphaMap;\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n *\n * referencePosition: <float>,\n * nearDistance: <float>,\n * farDistance: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>\n *\n * }\n */\n\n function MeshDistanceMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshDistanceMaterial';\n\n this.referencePosition = new Vector3();\n this.nearDistance = 1;\n this.farDistance = 1000;\n\n this.skinning = false;\n this.morphTargets = false;\n\n this.map = null;\n\n this.alphaMap = null;\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n MeshDistanceMaterial.prototype = Object.create( Material.prototype );\n MeshDistanceMaterial.prototype.constructor = MeshDistanceMaterial;\n\n MeshDistanceMaterial.prototype.isMeshDistanceMaterial = true;\n\n MeshDistanceMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.referencePosition.copy( source.referencePosition );\n this.nearDistance = source.nearDistance;\n this.farDistance = source.farDistance;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n\n this.map = source.map;\n\n this.alphaMap = source.alphaMap;\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLShadowMap( _renderer, _objects, maxTextureSize ) {\n\n var _frustum = new Frustum(),\n _projScreenMatrix = new Matrix4(),\n\n _shadowMapSize = new Vector2(),\n _maxShadowMapSize = new Vector2( maxTextureSize, maxTextureSize ),\n\n _lookTarget = new Vector3(),\n _lightPositionWorld = new Vector3(),\n\n _MorphingFlag = 1,\n _SkinningFlag = 2,\n\n _NumberOfMaterialVariants = ( _MorphingFlag | _SkinningFlag ) + 1,\n\n _depthMaterials = new Array( _NumberOfMaterialVariants ),\n _distanceMaterials = new Array( _NumberOfMaterialVariants ),\n\n _materialCache = {};\n\n var shadowSide = { 0: BackSide, 1: FrontSide, 2: DoubleSide };\n\n var cubeDirections = [\n new Vector3( 1, 0, 0 ), new Vector3( - 1, 0, 0 ), new Vector3( 0, 0, 1 ),\n new Vector3( 0, 0, - 1 ), new Vector3( 0, 1, 0 ), new Vector3( 0, - 1, 0 )\n ];\n\n var cubeUps = [\n new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ), new Vector3( 0, 1, 0 ),\n new Vector3( 0, 1, 0 ), new Vector3( 0, 0, 1 ), new Vector3( 0, 0, - 1 )\n ];\n\n var cube2DViewPorts = [\n new Vector4(), new Vector4(), new Vector4(),\n new Vector4(), new Vector4(), new Vector4()\n ];\n\n // init\n\n for ( var i = 0; i !== _NumberOfMaterialVariants; ++ i ) {\n\n var useMorphing = ( i & _MorphingFlag ) !== 0;\n var useSkinning = ( i & _SkinningFlag ) !== 0;\n\n var depthMaterial = new MeshDepthMaterial( {\n\n depthPacking: RGBADepthPacking,\n\n morphTargets: useMorphing,\n skinning: useSkinning\n\n } );\n\n _depthMaterials[ i ] = depthMaterial;\n\n //\n\n var distanceMaterial = new MeshDistanceMaterial( {\n\n morphTargets: useMorphing,\n skinning: useSkinning\n\n } );\n\n _distanceMaterials[ i ] = distanceMaterial;\n\n }\n\n //\n\n var scope = this;\n\n this.enabled = false;\n\n this.autoUpdate = true;\n this.needsUpdate = false;\n\n this.type = PCFShadowMap;\n\n this.render = function ( lights, scene, camera ) {\n\n if ( scope.enabled === false ) return;\n if ( scope.autoUpdate === false && scope.needsUpdate === false ) return;\n\n if ( lights.length === 0 ) return;\n\n // TODO Clean up (needed in case of contextlost)\n var _gl = _renderer.context;\n var _state = _renderer.state;\n\n // Set GL state for depth map.\n _state.disable( _gl.BLEND );\n _state.buffers.color.setClear( 1, 1, 1, 1 );\n _state.buffers.depth.setTest( true );\n _state.setScissorTest( false );\n\n // render depth map\n\n var faceCount;\n\n for ( var i = 0, il = lights.length; i < il; i ++ ) {\n\n var light = lights[ i ];\n var shadow = light.shadow;\n var isPointLight = light && light.isPointLight;\n\n if ( shadow === undefined ) {\n\n console.warn( 'THREE.WebGLShadowMap:', light, 'has no shadow.' );\n continue;\n\n }\n\n var shadowCamera = shadow.camera;\n\n _shadowMapSize.copy( shadow.mapSize );\n _shadowMapSize.min( _maxShadowMapSize );\n\n if ( isPointLight ) {\n\n var vpWidth = _shadowMapSize.x;\n var vpHeight = _shadowMapSize.y;\n\n // These viewports map a cube-map onto a 2D texture with the\n // following orientation:\n //\n // xzXZ\n // y Y\n //\n // X - Positive x direction\n // x - Negative x direction\n // Y - Positive y direction\n // y - Negative y direction\n // Z - Positive z direction\n // z - Negative z direction\n\n // positive X\n cube2DViewPorts[ 0 ].set( vpWidth * 2, vpHeight, vpWidth, vpHeight );\n // negative X\n cube2DViewPorts[ 1 ].set( 0, vpHeight, vpWidth, vpHeight );\n // positive Z\n cube2DViewPorts[ 2 ].set( vpWidth * 3, vpHeight, vpWidth, vpHeight );\n // negative Z\n cube2DViewPorts[ 3 ].set( vpWidth, vpHeight, vpWidth, vpHeight );\n // positive Y\n cube2DViewPorts[ 4 ].set( vpWidth * 3, 0, vpWidth, vpHeight );\n // negative Y\n cube2DViewPorts[ 5 ].set( vpWidth, 0, vpWidth, vpHeight );\n\n _shadowMapSize.x *= 4.0;\n _shadowMapSize.y *= 2.0;\n\n }\n\n if ( shadow.map === null ) {\n\n var pars = { minFilter: NearestFilter, magFilter: NearestFilter, format: RGBAFormat };\n\n shadow.map = new WebGLRenderTarget( _shadowMapSize.x, _shadowMapSize.y, pars );\n shadow.map.texture.name = light.name + \".shadowMap\";\n\n shadowCamera.updateProjectionMatrix();\n\n }\n\n if ( shadow.isSpotLightShadow ) {\n\n shadow.update( light );\n\n }\n\n var shadowMap = shadow.map;\n var shadowMatrix = shadow.matrix;\n\n _lightPositionWorld.setFromMatrixPosition( light.matrixWorld );\n shadowCamera.position.copy( _lightPositionWorld );\n\n if ( isPointLight ) {\n\n faceCount = 6;\n\n // for point lights we set the shadow matrix to be a translation-only matrix\n // equal to inverse of the light's position\n\n shadowMatrix.makeTranslation( - _lightPositionWorld.x, - _lightPositionWorld.y, - _lightPositionWorld.z );\n\n } else {\n\n faceCount = 1;\n\n _lookTarget.setFromMatrixPosition( light.target.matrixWorld );\n shadowCamera.lookAt( _lookTarget );\n shadowCamera.updateMatrixWorld();\n\n // compute shadow matrix\n\n shadowMatrix.set(\n 0.5, 0.0, 0.0, 0.5,\n 0.0, 0.5, 0.0, 0.5,\n 0.0, 0.0, 0.5, 0.5,\n 0.0, 0.0, 0.0, 1.0\n );\n\n shadowMatrix.multiply( shadowCamera.projectionMatrix );\n shadowMatrix.multiply( shadowCamera.matrixWorldInverse );\n\n }\n\n _renderer.setRenderTarget( shadowMap );\n _renderer.clear();\n\n // render shadow map for each cube face (if omni-directional) or\n // run a single pass if not\n\n for ( var face = 0; face < faceCount; face ++ ) {\n\n if ( isPointLight ) {\n\n _lookTarget.copy( shadowCamera.position );\n _lookTarget.add( cubeDirections[ face ] );\n shadowCamera.up.copy( cubeUps[ face ] );\n shadowCamera.lookAt( _lookTarget );\n shadowCamera.updateMatrixWorld();\n\n var vpDimensions = cube2DViewPorts[ face ];\n _state.viewport( vpDimensions );\n\n }\n\n // update camera matrices and frustum\n\n _projScreenMatrix.multiplyMatrices( shadowCamera.projectionMatrix, shadowCamera.matrixWorldInverse );\n _frustum.setFromMatrix( _projScreenMatrix );\n\n // set object matrices & frustum culling\n\n renderObject( scene, camera, shadowCamera, isPointLight );\n\n }\n\n }\n\n scope.needsUpdate = false;\n\n };\n\n function getDepthMaterial( object, material, isPointLight, lightPositionWorld, shadowCameraNear, shadowCameraFar ) {\n\n var geometry = object.geometry;\n\n var result = null;\n\n var materialVariants = _depthMaterials;\n var customMaterial = object.customDepthMaterial;\n\n if ( isPointLight ) {\n\n materialVariants = _distanceMaterials;\n customMaterial = object.customDistanceMaterial;\n\n }\n\n if ( ! customMaterial ) {\n\n var useMorphing = false;\n\n if ( material.morphTargets ) {\n\n if ( geometry && geometry.isBufferGeometry ) {\n\n useMorphing = geometry.morphAttributes && geometry.morphAttributes.position && geometry.morphAttributes.position.length > 0;\n\n } else if ( geometry && geometry.isGeometry ) {\n\n useMorphing = geometry.morphTargets && geometry.morphTargets.length > 0;\n\n }\n\n }\n\n if ( object.isSkinnedMesh && material.skinning === false ) {\n\n console.warn( 'THREE.WebGLShadowMap: THREE.SkinnedMesh with material.skinning set to false:', object );\n\n }\n\n var useSkinning = object.isSkinnedMesh && material.skinning;\n\n var variantIndex = 0;\n\n if ( useMorphing ) variantIndex |= _MorphingFlag;\n if ( useSkinning ) variantIndex |= _SkinningFlag;\n\n result = materialVariants[ variantIndex ];\n\n } else {\n\n result = customMaterial;\n\n }\n\n if ( _renderer.localClippingEnabled &&\n material.clipShadows === true &&\n material.clippingPlanes.length !== 0 ) {\n\n // in this case we need a unique material instance reflecting the\n // appropriate state\n\n var keyA = result.uuid, keyB = material.uuid;\n\n var materialsForVariant = _materialCache[ keyA ];\n\n if ( materialsForVariant === undefined ) {\n\n materialsForVariant = {};\n _materialCache[ keyA ] = materialsForVariant;\n\n }\n\n var cachedMaterial = materialsForVariant[ keyB ];\n\n if ( cachedMaterial === undefined ) {\n\n cachedMaterial = result.clone();\n materialsForVariant[ keyB ] = cachedMaterial;\n\n }\n\n result = cachedMaterial;\n\n }\n\n result.visible = material.visible;\n result.wireframe = material.wireframe;\n\n result.side = ( material.shadowSide != null ) ? material.shadowSide : shadowSide[ material.side ];\n\n result.clipShadows = material.clipShadows;\n result.clippingPlanes = material.clippingPlanes;\n result.clipIntersection = material.clipIntersection;\n\n result.wireframeLinewidth = material.wireframeLinewidth;\n result.linewidth = material.linewidth;\n\n if ( isPointLight && result.isMeshDistanceMaterial ) {\n\n result.referencePosition.copy( lightPositionWorld );\n result.nearDistance = shadowCameraNear;\n result.farDistance = shadowCameraFar;\n\n }\n\n return result;\n\n }\n\n function renderObject( object, camera, shadowCamera, isPointLight ) {\n\n if ( object.visible === false ) return;\n\n var visible = object.layers.test( camera.layers );\n\n if ( visible && ( object.isMesh || object.isLine || object.isPoints ) ) {\n\n if ( object.castShadow && ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) ) {\n\n object.modelViewMatrix.multiplyMatrices( shadowCamera.matrixWorldInverse, object.matrixWorld );\n\n var geometry = _objects.update( object );\n var material = object.material;\n\n if ( Array.isArray( material ) ) {\n\n var groups = geometry.groups;\n\n for ( var k = 0, kl = groups.length; k < kl; k ++ ) {\n\n var group = groups[ k ];\n var groupMaterial = material[ group.materialIndex ];\n\n if ( groupMaterial && groupMaterial.visible ) {\n\n var depthMaterial = getDepthMaterial( object, groupMaterial, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );\n _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, group );\n\n }\n\n }\n\n } else if ( material.visible ) {\n\n var depthMaterial = getDepthMaterial( object, material, isPointLight, _lightPositionWorld, shadowCamera.near, shadowCamera.far );\n _renderer.renderBufferDirect( shadowCamera, null, geometry, depthMaterial, object, null );\n\n }\n\n }\n\n }\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n renderObject( children[ i ], camera, shadowCamera, isPointLight );\n\n }\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CanvasTexture( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.needsUpdate = true;\n\n }\n\n CanvasTexture.prototype = Object.create( Texture.prototype );\n CanvasTexture.prototype.constructor = CanvasTexture;\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function WebGLSpriteRenderer( renderer, gl, state, textures, capabilities ) {\n\n var vertexBuffer, elementBuffer;\n var program, attributes, uniforms;\n\n var texture;\n\n // decompose matrixWorld\n\n var spritePosition = new Vector3();\n var spriteRotation = new Quaternion();\n var spriteScale = new Vector3();\n\n function init() {\n\n var vertices = new Float32Array( [\n - 0.5, - 0.5, 0, 0,\n 0.5, - 0.5, 1, 0,\n 0.5, 0.5, 1, 1,\n - 0.5, 0.5, 0, 1\n ] );\n\n var faces = new Uint16Array( [\n 0, 1, 2,\n 0, 2, 3\n ] );\n\n vertexBuffer = gl.createBuffer();\n elementBuffer = gl.createBuffer();\n\n gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n gl.bufferData( gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW );\n\n gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, faces, gl.STATIC_DRAW );\n\n program = createProgram();\n\n attributes = {\n position: gl.getAttribLocation( program, 'position' ),\n uv: gl.getAttribLocation( program, 'uv' )\n };\n\n uniforms = {\n uvOffset: gl.getUniformLocation( program, 'uvOffset' ),\n uvScale: gl.getUniformLocation( program, 'uvScale' ),\n\n rotation: gl.getUniformLocation( program, 'rotation' ),\n center: gl.getUniformLocation( program, 'center' ),\n scale: gl.getUniformLocation( program, 'scale' ),\n\n color: gl.getUniformLocation( program, 'color' ),\n map: gl.getUniformLocation( program, 'map' ),\n opacity: gl.getUniformLocation( program, 'opacity' ),\n\n modelViewMatrix: gl.getUniformLocation( program, 'modelViewMatrix' ),\n projectionMatrix: gl.getUniformLocation( program, 'projectionMatrix' ),\n\n fogType: gl.getUniformLocation( program, 'fogType' ),\n fogDensity: gl.getUniformLocation( program, 'fogDensity' ),\n fogNear: gl.getUniformLocation( program, 'fogNear' ),\n fogFar: gl.getUniformLocation( program, 'fogFar' ),\n fogColor: gl.getUniformLocation( program, 'fogColor' ),\n fogDepth: gl.getUniformLocation( program, 'fogDepth' ),\n\n alphaTest: gl.getUniformLocation( program, 'alphaTest' )\n };\n\n var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = 8;\n canvas.height = 8;\n\n var context = canvas.getContext( '2d' );\n context.fillStyle = 'white';\n context.fillRect( 0, 0, 8, 8 );\n\n texture = new CanvasTexture( canvas );\n\n }\n\n this.render = function ( sprites, scene, camera ) {\n\n if ( sprites.length === 0 ) return;\n\n // setup gl\n\n if ( program === undefined ) {\n\n init();\n\n }\n\n state.useProgram( program );\n\n state.initAttributes();\n state.enableAttribute( attributes.position );\n state.enableAttribute( attributes.uv );\n state.disableUnusedAttributes();\n\n state.disable( gl.CULL_FACE );\n state.enable( gl.BLEND );\n\n gl.bindBuffer( gl.ARRAY_BUFFER, vertexBuffer );\n gl.vertexAttribPointer( attributes.position, 2, gl.FLOAT, false, 2 * 8, 0 );\n gl.vertexAttribPointer( attributes.uv, 2, gl.FLOAT, false, 2 * 8, 8 );\n\n gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, elementBuffer );\n\n gl.uniformMatrix4fv( uniforms.projectionMatrix, false, camera.projectionMatrix.elements );\n\n state.activeTexture( gl.TEXTURE0 );\n gl.uniform1i( uniforms.map, 0 );\n\n var oldFogType = 0;\n var sceneFogType = 0;\n var fog = scene.fog;\n\n if ( fog ) {\n\n gl.uniform3f( uniforms.fogColor, fog.color.r, fog.color.g, fog.color.b );\n\n if ( fog.isFog ) {\n\n gl.uniform1f( uniforms.fogNear, fog.near );\n gl.uniform1f( uniforms.fogFar, fog.far );\n\n gl.uniform1i( uniforms.fogType, 1 );\n oldFogType = 1;\n sceneFogType = 1;\n\n } else if ( fog.isFogExp2 ) {\n\n gl.uniform1f( uniforms.fogDensity, fog.density );\n\n gl.uniform1i( uniforms.fogType, 2 );\n oldFogType = 2;\n sceneFogType = 2;\n\n }\n\n } else {\n\n gl.uniform1i( uniforms.fogType, 0 );\n oldFogType = 0;\n sceneFogType = 0;\n\n }\n\n\n // update positions and sort\n\n for ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n var sprite = sprites[ i ];\n\n sprite.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, sprite.matrixWorld );\n sprite.z = - sprite.modelViewMatrix.elements[ 14 ];\n\n }\n\n sprites.sort( painterSortStable );\n\n // render all sprites\n\n var scale = [];\n var center = [];\n\n for ( var i = 0, l = sprites.length; i < l; i ++ ) {\n\n var sprite = sprites[ i ];\n var material = sprite.material;\n\n if ( material.visible === false ) continue;\n\n sprite.onBeforeRender( renderer, scene, camera, undefined, material, undefined );\n\n gl.uniform1f( uniforms.alphaTest, material.alphaTest );\n gl.uniformMatrix4fv( uniforms.modelViewMatrix, false, sprite.modelViewMatrix.elements );\n\n sprite.matrixWorld.decompose( spritePosition, spriteRotation, spriteScale );\n\n scale[ 0 ] = spriteScale.x;\n scale[ 1 ] = spriteScale.y;\n\n center[ 0 ] = sprite.center.x - 0.5;\n center[ 1 ] = sprite.center.y - 0.5;\n\n var fogType = 0;\n\n if ( scene.fog && material.fog ) {\n\n fogType = sceneFogType;\n\n }\n\n if ( oldFogType !== fogType ) {\n\n gl.uniform1i( uniforms.fogType, fogType );\n oldFogType = fogType;\n\n }\n\n if ( material.map !== null ) {\n\n gl.uniform2f( uniforms.uvOffset, material.map.offset.x, material.map.offset.y );\n gl.uniform2f( uniforms.uvScale, material.map.repeat.x, material.map.repeat.y );\n\n } else {\n\n gl.uniform2f( uniforms.uvOffset, 0, 0 );\n gl.uniform2f( uniforms.uvScale, 1, 1 );\n\n }\n\n gl.uniform1f( uniforms.opacity, material.opacity );\n gl.uniform3f( uniforms.color, material.color.r, material.color.g, material.color.b );\n\n gl.uniform1f( uniforms.rotation, material.rotation );\n gl.uniform2fv( uniforms.center, center );\n gl.uniform2fv( uniforms.scale, scale );\n\n state.setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha );\n state.buffers.depth.setTest( material.depthTest );\n state.buffers.depth.setMask( material.depthWrite );\n state.buffers.color.setMask( material.colorWrite );\n\n textures.setTexture2D( material.map || texture, 0 );\n\n gl.drawElements( gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 );\n\n sprite.onAfterRender( renderer, scene, camera, undefined, material, undefined );\n\n }\n\n // restore gl\n\n state.enable( gl.CULL_FACE );\n\n state.reset();\n\n };\n\n function createProgram() {\n\n var program = gl.createProgram();\n\n var vertexShader = gl.createShader( gl.VERTEX_SHADER );\n var fragmentShader = gl.createShader( gl.FRAGMENT_SHADER );\n\n gl.shaderSource( vertexShader, [\n\n 'precision ' + capabilities.precision + ' float;',\n\n '#define SHADER_NAME ' + 'SpriteMaterial',\n\n 'uniform mat4 modelViewMatrix;',\n 'uniform mat4 projectionMatrix;',\n 'uniform float rotation;',\n 'uniform vec2 center;',\n 'uniform vec2 scale;',\n 'uniform vec2 uvOffset;',\n 'uniform vec2 uvScale;',\n\n 'attribute vec2 position;',\n 'attribute vec2 uv;',\n\n 'varying vec2 vUV;',\n 'varying float fogDepth;',\n\n 'void main() {',\n\n ' vUV = uvOffset + uv * uvScale;',\n\n ' vec2 alignedPosition = ( position - center ) * scale;',\n\n ' vec2 rotatedPosition;',\n ' rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;',\n ' rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;',\n\n ' vec4 mvPosition;',\n\n ' mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );',\n ' mvPosition.xy += rotatedPosition;',\n\n ' gl_Position = projectionMatrix * mvPosition;',\n\n ' fogDepth = - mvPosition.z;',\n\n '}'\n\n ].join( '\\n' ) );\n\n gl.shaderSource( fragmentShader, [\n\n 'precision ' + capabilities.precision + ' float;',\n\n '#define SHADER_NAME ' + 'SpriteMaterial',\n\n 'uniform vec3 color;',\n 'uniform sampler2D map;',\n 'uniform float opacity;',\n\n 'uniform int fogType;',\n 'uniform vec3 fogColor;',\n 'uniform float fogDensity;',\n 'uniform float fogNear;',\n 'uniform float fogFar;',\n 'uniform float alphaTest;',\n\n 'varying vec2 vUV;',\n 'varying float fogDepth;',\n\n 'void main() {',\n\n ' vec4 texture = texture2D( map, vUV );',\n\n ' gl_FragColor = vec4( color * texture.xyz, texture.a * opacity );',\n\n ' if ( gl_FragColor.a < alphaTest ) discard;',\n\n ' if ( fogType > 0 ) {',\n\n ' float fogFactor = 0.0;',\n\n ' if ( fogType == 1 ) {',\n\n ' fogFactor = smoothstep( fogNear, fogFar, fogDepth );',\n\n ' } else {',\n\n ' const float LOG2 = 1.442695;',\n ' fogFactor = exp2( - fogDensity * fogDensity * fogDepth * fogDepth * LOG2 );',\n ' fogFactor = 1.0 - clamp( fogFactor, 0.0, 1.0 );',\n\n ' }',\n\n ' gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor );',\n\n ' }',\n\n '}'\n\n ].join( '\\n' ) );\n\n gl.compileShader( vertexShader );\n gl.compileShader( fragmentShader );\n\n gl.attachShader( program, vertexShader );\n gl.attachShader( program, fragmentShader );\n\n gl.linkProgram( program );\n\n return program;\n\n }\n\n function painterSortStable( a, b ) {\n\n if ( a.renderOrder !== b.renderOrder ) {\n\n return a.renderOrder - b.renderOrder;\n\n } else if ( a.z !== b.z ) {\n\n return b.z - a.z;\n\n } else {\n\n return b.id - a.id;\n\n }\n\n }\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLState( gl, extensions, utils ) {\n\n function ColorBuffer() {\n\n var locked = false;\n\n var color = new Vector4();\n var currentColorMask = null;\n var currentColorClear = new Vector4( 0, 0, 0, 0 );\n\n return {\n\n setMask: function ( colorMask ) {\n\n if ( currentColorMask !== colorMask && ! locked ) {\n\n gl.colorMask( colorMask, colorMask, colorMask, colorMask );\n currentColorMask = colorMask;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( r, g, b, a, premultipliedAlpha ) {\n\n if ( premultipliedAlpha === true ) {\n\n r *= a; g *= a; b *= a;\n\n }\n\n color.set( r, g, b, a );\n\n if ( currentColorClear.equals( color ) === false ) {\n\n gl.clearColor( r, g, b, a );\n currentColorClear.copy( color );\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentColorMask = null;\n currentColorClear.set( - 1, 0, 0, 0 ); // set to invalid state\n\n }\n\n };\n\n }\n\n function DepthBuffer() {\n\n var locked = false;\n\n var currentDepthMask = null;\n var currentDepthFunc = null;\n var currentDepthClear = null;\n\n return {\n\n setTest: function ( depthTest ) {\n\n if ( depthTest ) {\n\n enable( gl.DEPTH_TEST );\n\n } else {\n\n disable( gl.DEPTH_TEST );\n\n }\n\n },\n\n setMask: function ( depthMask ) {\n\n if ( currentDepthMask !== depthMask && ! locked ) {\n\n gl.depthMask( depthMask );\n currentDepthMask = depthMask;\n\n }\n\n },\n\n setFunc: function ( depthFunc ) {\n\n if ( currentDepthFunc !== depthFunc ) {\n\n if ( depthFunc ) {\n\n switch ( depthFunc ) {\n\n case NeverDepth:\n\n gl.depthFunc( gl.NEVER );\n break;\n\n case AlwaysDepth:\n\n gl.depthFunc( gl.ALWAYS );\n break;\n\n case LessDepth:\n\n gl.depthFunc( gl.LESS );\n break;\n\n case LessEqualDepth:\n\n gl.depthFunc( gl.LEQUAL );\n break;\n\n case EqualDepth:\n\n gl.depthFunc( gl.EQUAL );\n break;\n\n case GreaterEqualDepth:\n\n gl.depthFunc( gl.GEQUAL );\n break;\n\n case GreaterDepth:\n\n gl.depthFunc( gl.GREATER );\n break;\n\n case NotEqualDepth:\n\n gl.depthFunc( gl.NOTEQUAL );\n break;\n\n default:\n\n gl.depthFunc( gl.LEQUAL );\n\n }\n\n } else {\n\n gl.depthFunc( gl.LEQUAL );\n\n }\n\n currentDepthFunc = depthFunc;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( depth ) {\n\n if ( currentDepthClear !== depth ) {\n\n gl.clearDepth( depth );\n currentDepthClear = depth;\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentDepthMask = null;\n currentDepthFunc = null;\n currentDepthClear = null;\n\n }\n\n };\n\n }\n\n function StencilBuffer() {\n\n var locked = false;\n\n var currentStencilMask = null;\n var currentStencilFunc = null;\n var currentStencilRef = null;\n var currentStencilFuncMask = null;\n var currentStencilFail = null;\n var currentStencilZFail = null;\n var currentStencilZPass = null;\n var currentStencilClear = null;\n\n return {\n\n setTest: function ( stencilTest ) {\n\n if ( stencilTest ) {\n\n enable( gl.STENCIL_TEST );\n\n } else {\n\n disable( gl.STENCIL_TEST );\n\n }\n\n },\n\n setMask: function ( stencilMask ) {\n\n if ( currentStencilMask !== stencilMask && ! locked ) {\n\n gl.stencilMask( stencilMask );\n currentStencilMask = stencilMask;\n\n }\n\n },\n\n setFunc: function ( stencilFunc, stencilRef, stencilMask ) {\n\n if ( currentStencilFunc !== stencilFunc ||\n currentStencilRef !== stencilRef ||\n currentStencilFuncMask !== stencilMask ) {\n\n gl.stencilFunc( stencilFunc, stencilRef, stencilMask );\n\n currentStencilFunc = stencilFunc;\n currentStencilRef = stencilRef;\n currentStencilFuncMask = stencilMask;\n\n }\n\n },\n\n setOp: function ( stencilFail, stencilZFail, stencilZPass ) {\n\n if ( currentStencilFail !== stencilFail ||\n currentStencilZFail !== stencilZFail ||\n currentStencilZPass !== stencilZPass ) {\n\n gl.stencilOp( stencilFail, stencilZFail, stencilZPass );\n\n currentStencilFail = stencilFail;\n currentStencilZFail = stencilZFail;\n currentStencilZPass = stencilZPass;\n\n }\n\n },\n\n setLocked: function ( lock ) {\n\n locked = lock;\n\n },\n\n setClear: function ( stencil ) {\n\n if ( currentStencilClear !== stencil ) {\n\n gl.clearStencil( stencil );\n currentStencilClear = stencil;\n\n }\n\n },\n\n reset: function () {\n\n locked = false;\n\n currentStencilMask = null;\n currentStencilFunc = null;\n currentStencilRef = null;\n currentStencilFuncMask = null;\n currentStencilFail = null;\n currentStencilZFail = null;\n currentStencilZPass = null;\n currentStencilClear = null;\n\n }\n\n };\n\n }\n\n //\n\n var colorBuffer = new ColorBuffer();\n var depthBuffer = new DepthBuffer();\n var stencilBuffer = new StencilBuffer();\n\n var maxVertexAttributes = gl.getParameter( gl.MAX_VERTEX_ATTRIBS );\n var newAttributes = new Uint8Array( maxVertexAttributes );\n var enabledAttributes = new Uint8Array( maxVertexAttributes );\n var attributeDivisors = new Uint8Array( maxVertexAttributes );\n\n var capabilities = {};\n\n var compressedTextureFormats = null;\n\n var currentProgram = null;\n\n var currentBlending = null;\n var currentBlendEquation = null;\n var currentBlendSrc = null;\n var currentBlendDst = null;\n var currentBlendEquationAlpha = null;\n var currentBlendSrcAlpha = null;\n var currentBlendDstAlpha = null;\n var currentPremultipledAlpha = false;\n\n var currentFlipSided = null;\n var currentCullFace = null;\n\n var currentLineWidth = null;\n\n var currentPolygonOffsetFactor = null;\n var currentPolygonOffsetUnits = null;\n\n var maxTextures = gl.getParameter( gl.MAX_COMBINED_TEXTURE_IMAGE_UNITS );\n\n var lineWidthAvailable = false;\n var version = 0;\n var glVersion = gl.getParameter( gl.VERSION );\n\n if ( glVersion.indexOf( 'WebGL' ) !== - 1 ) {\n\n version = parseFloat( /^WebGL\\ ([0-9])/.exec( glVersion )[ 1 ] );\n lineWidthAvailable = ( version >= 1.0 );\n\n } else if ( glVersion.indexOf( 'OpenGL ES' ) !== - 1 ) {\n\n version = parseFloat( /^OpenGL\\ ES\\ ([0-9])/.exec( glVersion )[ 1 ] );\n lineWidthAvailable = ( version >= 2.0 );\n\n }\n\n var currentTextureSlot = null;\n var currentBoundTextures = {};\n\n var currentScissor = new Vector4();\n var currentViewport = new Vector4();\n\n function createTexture( type, target, count ) {\n\n var data = new Uint8Array( 4 ); // 4 is required to match default unpack alignment of 4.\n var texture = gl.createTexture();\n\n gl.bindTexture( type, texture );\n gl.texParameteri( type, gl.TEXTURE_MIN_FILTER, gl.NEAREST );\n gl.texParameteri( type, gl.TEXTURE_MAG_FILTER, gl.NEAREST );\n\n for ( var i = 0; i < count; i ++ ) {\n\n gl.texImage2D( target + i, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, data );\n\n }\n\n return texture;\n\n }\n\n var emptyTextures = {};\n emptyTextures[ gl.TEXTURE_2D ] = createTexture( gl.TEXTURE_2D, gl.TEXTURE_2D, 1 );\n emptyTextures[ gl.TEXTURE_CUBE_MAP ] = createTexture( gl.TEXTURE_CUBE_MAP, gl.TEXTURE_CUBE_MAP_POSITIVE_X, 6 );\n\n // init\n\n colorBuffer.setClear( 0, 0, 0, 1 );\n depthBuffer.setClear( 1 );\n stencilBuffer.setClear( 0 );\n\n enable( gl.DEPTH_TEST );\n depthBuffer.setFunc( LessEqualDepth );\n\n setFlipSided( false );\n setCullFace( CullFaceBack );\n enable( gl.CULL_FACE );\n\n enable( gl.BLEND );\n setBlending( NormalBlending );\n\n //\n\n function initAttributes() {\n\n for ( var i = 0, l = newAttributes.length; i < l; i ++ ) {\n\n newAttributes[ i ] = 0;\n\n }\n\n }\n\n function enableAttribute( attribute ) {\n\n newAttributes[ attribute ] = 1;\n\n if ( enabledAttributes[ attribute ] === 0 ) {\n\n gl.enableVertexAttribArray( attribute );\n enabledAttributes[ attribute ] = 1;\n\n }\n\n if ( attributeDivisors[ attribute ] !== 0 ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n extension.vertexAttribDivisorANGLE( attribute, 0 );\n attributeDivisors[ attribute ] = 0;\n\n }\n\n }\n\n function enableAttributeAndDivisor( attribute, meshPerAttribute ) {\n\n newAttributes[ attribute ] = 1;\n\n if ( enabledAttributes[ attribute ] === 0 ) {\n\n gl.enableVertexAttribArray( attribute );\n enabledAttributes[ attribute ] = 1;\n\n }\n\n if ( attributeDivisors[ attribute ] !== meshPerAttribute ) {\n\n var extension = extensions.get( 'ANGLE_instanced_arrays' );\n\n extension.vertexAttribDivisorANGLE( attribute, meshPerAttribute );\n attributeDivisors[ attribute ] = meshPerAttribute;\n\n }\n\n }\n\n function disableUnusedAttributes() {\n\n for ( var i = 0, l = enabledAttributes.length; i !== l; ++ i ) {\n\n if ( enabledAttributes[ i ] !== newAttributes[ i ] ) {\n\n gl.disableVertexAttribArray( i );\n enabledAttributes[ i ] = 0;\n\n }\n\n }\n\n }\n\n function enable( id ) {\n\n if ( capabilities[ id ] !== true ) {\n\n gl.enable( id );\n capabilities[ id ] = true;\n\n }\n\n }\n\n function disable( id ) {\n\n if ( capabilities[ id ] !== false ) {\n\n gl.disable( id );\n capabilities[ id ] = false;\n\n }\n\n }\n\n function getCompressedTextureFormats() {\n\n if ( compressedTextureFormats === null ) {\n\n compressedTextureFormats = [];\n\n if ( extensions.get( 'WEBGL_compressed_texture_pvrtc' ) ||\n extensions.get( 'WEBGL_compressed_texture_s3tc' ) ||\n extensions.get( 'WEBGL_compressed_texture_etc1' ) ||\n extensions.get( 'WEBGL_compressed_texture_astc' ) ) {\n\n var formats = gl.getParameter( gl.COMPRESSED_TEXTURE_FORMATS );\n\n for ( var i = 0; i < formats.length; i ++ ) {\n\n compressedTextureFormats.push( formats[ i ] );\n\n }\n\n }\n\n }\n\n return compressedTextureFormats;\n\n }\n\n function useProgram( program ) {\n\n if ( currentProgram !== program ) {\n\n gl.useProgram( program );\n\n currentProgram = program;\n\n return true;\n\n }\n\n return false;\n\n }\n\n function setBlending( blending, blendEquation, blendSrc, blendDst, blendEquationAlpha, blendSrcAlpha, blendDstAlpha, premultipliedAlpha ) {\n\n if ( blending !== NoBlending ) {\n\n enable( gl.BLEND );\n\n } else {\n\n disable( gl.BLEND );\n\n }\n\n if ( blending !== CustomBlending ) {\n\n if ( blending !== currentBlending || premultipliedAlpha !== currentPremultipledAlpha ) {\n\n switch ( blending ) {\n\n case AdditiveBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ONE, gl.ONE, gl.ONE, gl.ONE );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.SRC_ALPHA, gl.ONE );\n\n }\n break;\n\n case SubtractiveBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ZERO, gl.ZERO, gl.ONE_MINUS_SRC_COLOR, gl.ONE_MINUS_SRC_ALPHA );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.ZERO, gl.ONE_MINUS_SRC_COLOR );\n\n }\n break;\n\n case MultiplyBlending:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ZERO, gl.SRC_COLOR, gl.ZERO, gl.SRC_ALPHA );\n\n } else {\n\n gl.blendEquation( gl.FUNC_ADD );\n gl.blendFunc( gl.ZERO, gl.SRC_COLOR );\n\n }\n break;\n\n default:\n\n if ( premultipliedAlpha ) {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.ONE, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n } else {\n\n gl.blendEquationSeparate( gl.FUNC_ADD, gl.FUNC_ADD );\n gl.blendFuncSeparate( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA );\n\n }\n\n }\n\n }\n\n currentBlendEquation = null;\n currentBlendSrc = null;\n currentBlendDst = null;\n currentBlendEquationAlpha = null;\n currentBlendSrcAlpha = null;\n currentBlendDstAlpha = null;\n\n } else {\n\n blendEquationAlpha = blendEquationAlpha || blendEquation;\n blendSrcAlpha = blendSrcAlpha || blendSrc;\n blendDstAlpha = blendDstAlpha || blendDst;\n\n if ( blendEquation !== currentBlendEquation || blendEquationAlpha !== currentBlendEquationAlpha ) {\n\n gl.blendEquationSeparate( utils.convert( blendEquation ), utils.convert( blendEquationAlpha ) );\n\n currentBlendEquation = blendEquation;\n currentBlendEquationAlpha = blendEquationAlpha;\n\n }\n\n if ( blendSrc !== currentBlendSrc || blendDst !== currentBlendDst || blendSrcAlpha !== currentBlendSrcAlpha || blendDstAlpha !== currentBlendDstAlpha ) {\n\n gl.blendFuncSeparate( utils.convert( blendSrc ), utils.convert( blendDst ), utils.convert( blendSrcAlpha ), utils.convert( blendDstAlpha ) );\n\n currentBlendSrc = blendSrc;\n currentBlendDst = blendDst;\n currentBlendSrcAlpha = blendSrcAlpha;\n currentBlendDstAlpha = blendDstAlpha;\n\n }\n\n }\n\n currentBlending = blending;\n currentPremultipledAlpha = premultipliedAlpha;\n\n }\n\n function setMaterial( material, frontFaceCW ) {\n\n material.side === DoubleSide\n ? disable( gl.CULL_FACE )\n : enable( gl.CULL_FACE );\n\n var flipSided = ( material.side === BackSide );\n if ( frontFaceCW ) flipSided = ! flipSided;\n\n setFlipSided( flipSided );\n\n material.transparent === true\n ? setBlending( material.blending, material.blendEquation, material.blendSrc, material.blendDst, material.blendEquationAlpha, material.blendSrcAlpha, material.blendDstAlpha, material.premultipliedAlpha )\n : setBlending( NoBlending );\n\n depthBuffer.setFunc( material.depthFunc );\n depthBuffer.setTest( material.depthTest );\n depthBuffer.setMask( material.depthWrite );\n colorBuffer.setMask( material.colorWrite );\n\n setPolygonOffset( material.polygonOffset, material.polygonOffsetFactor, material.polygonOffsetUnits );\n\n }\n\n //\n\n function setFlipSided( flipSided ) {\n\n if ( currentFlipSided !== flipSided ) {\n\n if ( flipSided ) {\n\n gl.frontFace( gl.CW );\n\n } else {\n\n gl.frontFace( gl.CCW );\n\n }\n\n currentFlipSided = flipSided;\n\n }\n\n }\n\n function setCullFace( cullFace ) {\n\n if ( cullFace !== CullFaceNone ) {\n\n enable( gl.CULL_FACE );\n\n if ( cullFace !== currentCullFace ) {\n\n if ( cullFace === CullFaceBack ) {\n\n gl.cullFace( gl.BACK );\n\n } else if ( cullFace === CullFaceFront ) {\n\n gl.cullFace( gl.FRONT );\n\n } else {\n\n gl.cullFace( gl.FRONT_AND_BACK );\n\n }\n\n }\n\n } else {\n\n disable( gl.CULL_FACE );\n\n }\n\n currentCullFace = cullFace;\n\n }\n\n function setLineWidth( width ) {\n\n if ( width !== currentLineWidth ) {\n\n if ( lineWidthAvailable ) gl.lineWidth( width );\n\n currentLineWidth = width;\n\n }\n\n }\n\n function setPolygonOffset( polygonOffset, factor, units ) {\n\n if ( polygonOffset ) {\n\n enable( gl.POLYGON_OFFSET_FILL );\n\n if ( currentPolygonOffsetFactor !== factor || currentPolygonOffsetUnits !== units ) {\n\n gl.polygonOffset( factor, units );\n\n currentPolygonOffsetFactor = factor;\n currentPolygonOffsetUnits = units;\n\n }\n\n } else {\n\n disable( gl.POLYGON_OFFSET_FILL );\n\n }\n\n }\n\n function setScissorTest( scissorTest ) {\n\n if ( scissorTest ) {\n\n enable( gl.SCISSOR_TEST );\n\n } else {\n\n disable( gl.SCISSOR_TEST );\n\n }\n\n }\n\n // texture\n\n function activeTexture( webglSlot ) {\n\n if ( webglSlot === undefined ) webglSlot = gl.TEXTURE0 + maxTextures - 1;\n\n if ( currentTextureSlot !== webglSlot ) {\n\n gl.activeTexture( webglSlot );\n currentTextureSlot = webglSlot;\n\n }\n\n }\n\n function bindTexture( webglType, webglTexture ) {\n\n if ( currentTextureSlot === null ) {\n\n activeTexture();\n\n }\n\n var boundTexture = currentBoundTextures[ currentTextureSlot ];\n\n if ( boundTexture === undefined ) {\n\n boundTexture = { type: undefined, texture: undefined };\n currentBoundTextures[ currentTextureSlot ] = boundTexture;\n\n }\n\n if ( boundTexture.type !== webglType || boundTexture.texture !== webglTexture ) {\n\n gl.bindTexture( webglType, webglTexture || emptyTextures[ webglType ] );\n\n boundTexture.type = webglType;\n boundTexture.texture = webglTexture;\n\n }\n\n }\n\n function compressedTexImage2D() {\n\n try {\n\n gl.compressedTexImage2D.apply( gl, arguments );\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLState:', error );\n\n }\n\n }\n\n function texImage2D() {\n\n try {\n\n gl.texImage2D.apply( gl, arguments );\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLState:', error );\n\n }\n\n }\n\n //\n\n function scissor( scissor ) {\n\n if ( currentScissor.equals( scissor ) === false ) {\n\n gl.scissor( scissor.x, scissor.y, scissor.z, scissor.w );\n currentScissor.copy( scissor );\n\n }\n\n }\n\n function viewport( viewport ) {\n\n if ( currentViewport.equals( viewport ) === false ) {\n\n gl.viewport( viewport.x, viewport.y, viewport.z, viewport.w );\n currentViewport.copy( viewport );\n\n }\n\n }\n\n //\n\n function reset() {\n\n for ( var i = 0; i < enabledAttributes.length; i ++ ) {\n\n if ( enabledAttributes[ i ] === 1 ) {\n\n gl.disableVertexAttribArray( i );\n enabledAttributes[ i ] = 0;\n\n }\n\n }\n\n capabilities = {};\n\n compressedTextureFormats = null;\n\n currentTextureSlot = null;\n currentBoundTextures = {};\n\n currentProgram = null;\n\n currentBlending = null;\n\n currentFlipSided = null;\n currentCullFace = null;\n\n colorBuffer.reset();\n depthBuffer.reset();\n stencilBuffer.reset();\n\n }\n\n return {\n\n buffers: {\n color: colorBuffer,\n depth: depthBuffer,\n stencil: stencilBuffer\n },\n\n initAttributes: initAttributes,\n enableAttribute: enableAttribute,\n enableAttributeAndDivisor: enableAttributeAndDivisor,\n disableUnusedAttributes: disableUnusedAttributes,\n enable: enable,\n disable: disable,\n getCompressedTextureFormats: getCompressedTextureFormats,\n\n useProgram: useProgram,\n\n setBlending: setBlending,\n setMaterial: setMaterial,\n\n setFlipSided: setFlipSided,\n setCullFace: setCullFace,\n\n setLineWidth: setLineWidth,\n setPolygonOffset: setPolygonOffset,\n\n setScissorTest: setScissorTest,\n\n activeTexture: activeTexture,\n bindTexture: bindTexture,\n compressedTexImage2D: compressedTexImage2D,\n texImage2D: texImage2D,\n\n scissor: scissor,\n viewport: viewport,\n\n reset: reset\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info ) {\n\n var _isWebGL2 = ( typeof WebGL2RenderingContext !== 'undefined' && _gl instanceof WebGL2RenderingContext ); /* global WebGL2RenderingContext */\n var _videoTextures = {};\n var _canvas;\n\n //\n\n function clampToMaxSize( image, maxSize ) {\n\n if ( image.width > maxSize || image.height > maxSize ) {\n\n if ( 'data' in image ) {\n\n console.warn( 'THREE.WebGLRenderer: image in DataTexture is too big (' + image.width + 'x' + image.height + ').' );\n return;\n\n }\n\n // Warning: Scaling through the canvas will only work with images that use\n // premultiplied alpha.\n\n var scale = maxSize / Math.max( image.width, image.height );\n\n var canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n canvas.width = Math.floor( image.width * scale );\n canvas.height = Math.floor( image.height * scale );\n\n var context = canvas.getContext( '2d' );\n context.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height );\n\n console.warn( 'THREE.WebGLRenderer: image is too big (' + image.width + 'x' + image.height + '). Resized to ' + canvas.width + 'x' + canvas.height, image );\n\n return canvas;\n\n }\n\n return image;\n\n }\n\n function isPowerOfTwo( image ) {\n\n return _Math.isPowerOfTwo( image.width ) && _Math.isPowerOfTwo( image.height );\n\n }\n\n function makePowerOfTwo( image ) {\n\n if ( image instanceof HTMLImageElement || image instanceof HTMLCanvasElement || image instanceof ImageBitmap ) {\n\n if ( _canvas === undefined ) _canvas = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n\n _canvas.width = _Math.floorPowerOfTwo( image.width );\n _canvas.height = _Math.floorPowerOfTwo( image.height );\n\n var context = _canvas.getContext( '2d' );\n context.drawImage( image, 0, 0, _canvas.width, _canvas.height );\n\n console.warn( 'THREE.WebGLRenderer: image is not power of two (' + image.width + 'x' + image.height + '). Resized to ' + _canvas.width + 'x' + _canvas.height, image );\n\n return _canvas;\n\n }\n\n return image;\n\n }\n\n function textureNeedsPowerOfTwo( texture ) {\n\n return ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) ||\n ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter );\n\n }\n\n function textureNeedsGenerateMipmaps( texture, isPowerOfTwo ) {\n\n return texture.generateMipmaps && isPowerOfTwo &&\n texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter;\n\n }\n\n function generateMipmap( target, texture, width, height ) {\n\n _gl.generateMipmap( target );\n\n var textureProperties = properties.get( texture );\n textureProperties.__maxMipLevel = Math.log2( Math.max( width, height ) );\n\n }\n\n // Fallback filters for non-power-of-2 textures\n\n function filterFallback( f ) {\n\n if ( f === NearestFilter || f === NearestMipMapNearestFilter || f === NearestMipMapLinearFilter ) {\n\n return _gl.NEAREST;\n\n }\n\n return _gl.LINEAR;\n\n }\n\n //\n\n function onTextureDispose( event ) {\n\n var texture = event.target;\n\n texture.removeEventListener( 'dispose', onTextureDispose );\n\n deallocateTexture( texture );\n\n if ( texture.isVideoTexture ) {\n\n delete _videoTextures[ texture.id ];\n\n }\n\n info.memory.textures --;\n\n }\n\n function onRenderTargetDispose( event ) {\n\n var renderTarget = event.target;\n\n renderTarget.removeEventListener( 'dispose', onRenderTargetDispose );\n\n deallocateRenderTarget( renderTarget );\n\n info.memory.textures --;\n\n }\n\n //\n\n function deallocateTexture( texture ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.image && textureProperties.__image__webglTextureCube ) {\n\n // cube texture\n\n _gl.deleteTexture( textureProperties.__image__webglTextureCube );\n\n } else {\n\n // 2D texture\n\n if ( textureProperties.__webglInit === undefined ) return;\n\n _gl.deleteTexture( textureProperties.__webglTexture );\n\n }\n\n // remove all webgl properties\n properties.remove( texture );\n\n }\n\n function deallocateRenderTarget( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n var textureProperties = properties.get( renderTarget.texture );\n\n if ( ! renderTarget ) return;\n\n if ( textureProperties.__webglTexture !== undefined ) {\n\n _gl.deleteTexture( textureProperties.__webglTexture );\n\n }\n\n if ( renderTarget.depthTexture ) {\n\n renderTarget.depthTexture.dispose();\n\n }\n\n if ( renderTarget.isWebGLRenderTargetCube ) {\n\n for ( var i = 0; i < 6; i ++ ) {\n\n _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] );\n if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] );\n\n }\n\n } else {\n\n _gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer );\n if ( renderTargetProperties.__webglDepthbuffer ) _gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer );\n\n }\n\n properties.remove( renderTarget.texture );\n properties.remove( renderTarget );\n\n }\n\n //\n\n\n\n function setTexture2D( texture, slot ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.isVideoTexture ) updateVideoTexture( texture );\n\n if ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n var image = texture.image;\n\n if ( image === undefined ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is undefined', texture );\n\n } else if ( image.complete === false ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture marked for update but image is incomplete', texture );\n\n } else {\n\n uploadTexture( textureProperties, texture, slot );\n return;\n\n }\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n }\n\n function setTextureCube( texture, slot ) {\n\n var textureProperties = properties.get( texture );\n\n if ( texture.image.length === 6 ) {\n\n if ( texture.version > 0 && textureProperties.__version !== texture.version ) {\n\n if ( ! textureProperties.__image__webglTextureCube ) {\n\n texture.addEventListener( 'dispose', onTextureDispose );\n\n textureProperties.__image__webglTextureCube = _gl.createTexture();\n\n info.memory.textures ++;\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n\n var isCompressed = ( texture && texture.isCompressedTexture );\n var isDataTexture = ( texture.image[ 0 ] && texture.image[ 0 ].isDataTexture );\n\n var cubeImage = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( ! isCompressed && ! isDataTexture ) {\n\n cubeImage[ i ] = clampToMaxSize( texture.image[ i ], capabilities.maxCubemapSize );\n\n } else {\n\n cubeImage[ i ] = isDataTexture ? texture.image[ i ].image : texture.image[ i ];\n\n }\n\n }\n\n var image = cubeImage[ 0 ],\n isPowerOfTwoImage = isPowerOfTwo( image ),\n glFormat = utils.convert( texture.format ),\n glType = utils.convert( texture.type );\n\n setTextureParameters( _gl.TEXTURE_CUBE_MAP, texture, isPowerOfTwoImage );\n\n for ( var i = 0; i < 6; i ++ ) {\n\n if ( ! isCompressed ) {\n\n if ( isDataTexture ) {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, cubeImage[ i ].width, cubeImage[ i ].height, 0, glFormat, glType, cubeImage[ i ].data );\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glFormat, glFormat, glType, cubeImage[ i ] );\n\n }\n\n } else {\n\n var mipmap, mipmaps = cubeImage[ i ].mipmaps;\n\n for ( var j = 0, jl = mipmaps.length; j < jl; j ++ ) {\n\n mipmap = mipmaps[ j ];\n\n if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n state.compressedTexImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n } else {\n\n console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .setTextureCube()' );\n\n }\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i, j, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n }\n\n }\n\n }\n\n if ( ! isCompressed ) {\n\n textureProperties.__maxMipLevel = 0;\n\n } else {\n\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n }\n\n if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {\n\n // We assume images for cube map have the same size.\n generateMipmap( _gl.TEXTURE_CUBE_MAP, texture, image.width, image.height );\n\n }\n\n textureProperties.__version = texture.version;\n\n if ( texture.onUpdate ) texture.onUpdate( texture );\n\n } else {\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__image__webglTextureCube );\n\n }\n\n }\n\n }\n\n function setTextureCubeDynamic( texture, slot ) {\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, properties.get( texture ).__webglTexture );\n\n }\n\n function setTextureParameters( textureType, texture, isPowerOfTwoImage ) {\n\n var extension;\n\n if ( isPowerOfTwoImage ) {\n\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, utils.convert( texture.wrapS ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, utils.convert( texture.wrapT ) );\n\n _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, utils.convert( texture.magFilter ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, utils.convert( texture.minFilter ) );\n\n } else {\n\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_S, _gl.CLAMP_TO_EDGE );\n _gl.texParameteri( textureType, _gl.TEXTURE_WRAP_T, _gl.CLAMP_TO_EDGE );\n\n if ( texture.wrapS !== ClampToEdgeWrapping || texture.wrapT !== ClampToEdgeWrapping ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.wrapS and Texture.wrapT should be set to THREE.ClampToEdgeWrapping.', texture );\n\n }\n\n _gl.texParameteri( textureType, _gl.TEXTURE_MAG_FILTER, filterFallback( texture.magFilter ) );\n _gl.texParameteri( textureType, _gl.TEXTURE_MIN_FILTER, filterFallback( texture.minFilter ) );\n\n if ( texture.minFilter !== NearestFilter && texture.minFilter !== LinearFilter ) {\n\n console.warn( 'THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.', texture );\n\n }\n\n }\n\n extension = extensions.get( 'EXT_texture_filter_anisotropic' );\n\n if ( extension ) {\n\n if ( texture.type === FloatType && extensions.get( 'OES_texture_float_linear' ) === null ) return;\n if ( texture.type === HalfFloatType && extensions.get( 'OES_texture_half_float_linear' ) === null ) return;\n\n if ( texture.anisotropy > 1 || properties.get( texture ).__currentAnisotropy ) {\n\n _gl.texParameterf( textureType, extension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min( texture.anisotropy, capabilities.getMaxAnisotropy() ) );\n properties.get( texture ).__currentAnisotropy = texture.anisotropy;\n\n }\n\n }\n\n }\n\n function uploadTexture( textureProperties, texture, slot ) {\n\n if ( textureProperties.__webglInit === undefined ) {\n\n textureProperties.__webglInit = true;\n\n texture.addEventListener( 'dispose', onTextureDispose );\n\n textureProperties.__webglTexture = _gl.createTexture();\n\n info.memory.textures ++;\n\n }\n\n state.activeTexture( _gl.TEXTURE0 + slot );\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n\n _gl.pixelStorei( _gl.UNPACK_FLIP_Y_WEBGL, texture.flipY );\n _gl.pixelStorei( _gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, texture.premultiplyAlpha );\n _gl.pixelStorei( _gl.UNPACK_ALIGNMENT, texture.unpackAlignment );\n\n var image = clampToMaxSize( texture.image, capabilities.maxTextureSize );\n\n if ( textureNeedsPowerOfTwo( texture ) && isPowerOfTwo( image ) === false ) {\n\n image = makePowerOfTwo( image );\n\n }\n\n var isPowerOfTwoImage = isPowerOfTwo( image ),\n glFormat = utils.convert( texture.format ),\n glType = utils.convert( texture.type );\n\n setTextureParameters( _gl.TEXTURE_2D, texture, isPowerOfTwoImage );\n\n var mipmap, mipmaps = texture.mipmaps;\n\n if ( texture.isDepthTexture ) {\n\n // populate depth texture with dummy data\n\n var internalFormat = _gl.DEPTH_COMPONENT;\n\n if ( texture.type === FloatType ) {\n\n if ( ! _isWebGL2 ) throw new Error( 'Float Depth Texture only supported in WebGL2.0' );\n internalFormat = _gl.DEPTH_COMPONENT32F;\n\n } else if ( _isWebGL2 ) {\n\n // WebGL 2.0 requires signed internalformat for glTexImage2D\n internalFormat = _gl.DEPTH_COMPONENT16;\n\n }\n\n if ( texture.format === DepthFormat && internalFormat === _gl.DEPTH_COMPONENT ) {\n\n // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n // DEPTH_COMPONENT and type is not UNSIGNED_SHORT or UNSIGNED_INT\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.type !== UnsignedShortType && texture.type !== UnsignedIntType ) {\n\n console.warn( 'THREE.WebGLRenderer: Use UnsignedShortType or UnsignedIntType for DepthFormat DepthTexture.' );\n\n texture.type = UnsignedShortType;\n glType = utils.convert( texture.type );\n\n }\n\n }\n\n // Depth stencil textures need the DEPTH_STENCIL internal format\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.format === DepthStencilFormat ) {\n\n internalFormat = _gl.DEPTH_STENCIL;\n\n // The error INVALID_OPERATION is generated by texImage2D if format and internalformat are\n // DEPTH_STENCIL and type is not UNSIGNED_INT_24_8_WEBGL.\n // (https://www.khronos.org/registry/webgl/extensions/WEBGL_depth_texture/)\n if ( texture.type !== UnsignedInt248Type ) {\n\n console.warn( 'THREE.WebGLRenderer: Use UnsignedInt248Type for DepthStencilFormat DepthTexture.' );\n\n texture.type = UnsignedInt248Type;\n glType = utils.convert( texture.type );\n\n }\n\n }\n\n state.texImage2D( _gl.TEXTURE_2D, 0, internalFormat, image.width, image.height, 0, glFormat, glType, null );\n\n } else if ( texture.isDataTexture ) {\n\n // use manually created mipmaps if available\n // if there are no manual mipmaps\n // set 0 level mipmap and then use GL to generate other mipmap levels\n\n if ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n texture.generateMipmaps = false;\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, image.width, image.height, 0, glFormat, glType, image.data );\n textureProperties.__maxMipLevel = 0;\n\n }\n\n } else if ( texture.isCompressedTexture ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n\n if ( texture.format !== RGBAFormat && texture.format !== RGBFormat ) {\n\n if ( state.getCompressedTextureFormats().indexOf( glFormat ) > - 1 ) {\n\n state.compressedTexImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, mipmap.data );\n\n } else {\n\n console.warn( 'THREE.WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()' );\n\n }\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, mipmap.width, mipmap.height, 0, glFormat, glType, mipmap.data );\n\n }\n\n }\n\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n // regular Texture (image, video, canvas)\n\n // use manually created mipmaps if available\n // if there are no manual mipmaps\n // set 0 level mipmap and then use GL to generate other mipmap levels\n\n if ( mipmaps.length > 0 && isPowerOfTwoImage ) {\n\n for ( var i = 0, il = mipmaps.length; i < il; i ++ ) {\n\n mipmap = mipmaps[ i ];\n state.texImage2D( _gl.TEXTURE_2D, i, glFormat, glFormat, glType, mipmap );\n\n }\n\n texture.generateMipmaps = false;\n textureProperties.__maxMipLevel = mipmaps.length - 1;\n\n } else {\n\n state.texImage2D( _gl.TEXTURE_2D, 0, glFormat, glFormat, glType, image );\n textureProperties.__maxMipLevel = 0;\n\n }\n\n }\n\n if ( textureNeedsGenerateMipmaps( texture, isPowerOfTwoImage ) ) {\n\n generateMipmap( _gl.TEXTURE_2D, texture, image.width, image.height );\n\n }\n\n textureProperties.__version = texture.version;\n\n if ( texture.onUpdate ) texture.onUpdate( texture );\n\n }\n\n // Render targets\n\n // Setup storage for target texture and bind it to correct framebuffer\n function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {\n\n var glFormat = utils.convert( renderTarget.texture.format );\n var glType = utils.convert( renderTarget.texture.type );\n state.texImage2D( textureTarget, 0, glFormat, renderTarget.width, renderTarget.height, 0, glFormat, glType, null );\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, attachment, textureTarget, properties.get( renderTarget.texture ).__webglTexture, 0 );\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n }\n\n // Setup storage for internal depth/stencil buffers and bind to correct framebuffer\n function setupRenderBufferStorage( renderbuffer, renderTarget ) {\n\n _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer );\n\n if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) {\n\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height );\n _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) {\n\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height );\n _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer );\n\n } else {\n\n // FIXME: We don't support !depth !stencil\n _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height );\n\n }\n\n _gl.bindRenderbuffer( _gl.RENDERBUFFER, null );\n\n }\n\n // Setup resources for a Depth Texture for a FBO (needs an extension)\n function setupDepthTexture( framebuffer, renderTarget ) {\n\n var isCube = ( renderTarget && renderTarget.isWebGLRenderTargetCube );\n if ( isCube ) throw new Error( 'Depth Texture with cube render targets is not supported' );\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n if ( ! ( renderTarget.depthTexture && renderTarget.depthTexture.isDepthTexture ) ) {\n\n throw new Error( 'renderTarget.depthTexture must be an instance of THREE.DepthTexture' );\n\n }\n\n // upload an empty depth texture with framebuffer size\n if ( ! properties.get( renderTarget.depthTexture ).__webglTexture ||\n renderTarget.depthTexture.image.width !== renderTarget.width ||\n renderTarget.depthTexture.image.height !== renderTarget.height ) {\n\n renderTarget.depthTexture.image.width = renderTarget.width;\n renderTarget.depthTexture.image.height = renderTarget.height;\n renderTarget.depthTexture.needsUpdate = true;\n\n }\n\n setTexture2D( renderTarget.depthTexture, 0 );\n\n var webglDepthTexture = properties.get( renderTarget.depthTexture ).__webglTexture;\n\n if ( renderTarget.depthTexture.format === DepthFormat ) {\n\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n } else if ( renderTarget.depthTexture.format === DepthStencilFormat ) {\n\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.TEXTURE_2D, webglDepthTexture, 0 );\n\n } else {\n\n throw new Error( 'Unknown depthTexture format' );\n\n }\n\n }\n\n // Setup GL resources for a non-texture depth buffer\n function setupDepthRenderbuffer( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n\n var isCube = ( renderTarget.isWebGLRenderTargetCube === true );\n\n if ( renderTarget.depthTexture ) {\n\n if ( isCube ) throw new Error( 'target.depthTexture not supported in Cube render targets' );\n\n setupDepthTexture( renderTargetProperties.__webglFramebuffer, renderTarget );\n\n } else {\n\n if ( isCube ) {\n\n renderTargetProperties.__webglDepthbuffer = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer[ i ] );\n renderTargetProperties.__webglDepthbuffer[ i ] = _gl.createRenderbuffer();\n setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer[ i ], renderTarget );\n\n }\n\n } else {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglFramebuffer );\n renderTargetProperties.__webglDepthbuffer = _gl.createRenderbuffer();\n setupRenderBufferStorage( renderTargetProperties.__webglDepthbuffer, renderTarget );\n\n }\n\n }\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, null );\n\n }\n\n // Set up GL resources for the render target\n function setupRenderTarget( renderTarget ) {\n\n var renderTargetProperties = properties.get( renderTarget );\n var textureProperties = properties.get( renderTarget.texture );\n\n renderTarget.addEventListener( 'dispose', onRenderTargetDispose );\n\n textureProperties.__webglTexture = _gl.createTexture();\n\n info.memory.textures ++;\n\n var isCube = ( renderTarget.isWebGLRenderTargetCube === true );\n var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );\n\n // Setup framebuffer\n\n if ( isCube ) {\n\n renderTargetProperties.__webglFramebuffer = [];\n\n for ( var i = 0; i < 6; i ++ ) {\n\n renderTargetProperties.__webglFramebuffer[ i ] = _gl.createFramebuffer();\n\n }\n\n } else {\n\n renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();\n\n }\n\n // Setup color buffer\n\n if ( isCube ) {\n\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, textureProperties.__webglTexture );\n setTextureParameters( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, isTargetPowerOfTwo );\n\n for ( var i = 0; i < 6; i ++ ) {\n\n setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );\n\n }\n\n if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {\n\n generateMipmap( _gl.TEXTURE_CUBE_MAP, renderTarget.texture, renderTarget.width, renderTarget.height );\n\n }\n\n state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );\n\n } else {\n\n state.bindTexture( _gl.TEXTURE_2D, textureProperties.__webglTexture );\n setTextureParameters( _gl.TEXTURE_2D, renderTarget.texture, isTargetPowerOfTwo );\n setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_2D );\n\n if ( textureNeedsGenerateMipmaps( renderTarget.texture, isTargetPowerOfTwo ) ) {\n\n generateMipmap( _gl.TEXTURE_2D, renderTarget.texture, renderTarget.width, renderTarget.height );\n\n }\n\n state.bindTexture( _gl.TEXTURE_2D, null );\n\n }\n\n // Setup depth and stencil buffers\n\n if ( renderTarget.depthBuffer ) {\n\n setupDepthRenderbuffer( renderTarget );\n\n }\n\n }\n\n function updateRenderTargetMipmap( renderTarget ) {\n\n var texture = renderTarget.texture;\n var isTargetPowerOfTwo = isPowerOfTwo( renderTarget );\n\n if ( textureNeedsGenerateMipmaps( texture, isTargetPowerOfTwo ) ) {\n\n var target = renderTarget.isWebGLRenderTargetCube ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;\n var webglTexture = properties.get( texture ).__webglTexture;\n\n state.bindTexture( target, webglTexture );\n generateMipmap( target, texture, renderTarget.width, renderTarget.height );\n state.bindTexture( target, null );\n\n }\n\n }\n\n function updateVideoTexture( texture ) {\n\n var id = texture.id;\n var frame = info.render.frame;\n\n // Check the last frame we updated the VideoTexture\n\n if ( _videoTextures[ id ] !== frame ) {\n\n _videoTextures[ id ] = frame;\n texture.update();\n\n }\n\n }\n\n this.setTexture2D = setTexture2D;\n this.setTextureCube = setTextureCube;\n this.setTextureCubeDynamic = setTextureCubeDynamic;\n this.setupRenderTarget = setupRenderTarget;\n this.updateRenderTargetMipmap = updateRenderTargetMipmap;\n\n }\n\n /**\n * @author thespite / http://www.twitter.com/thespite\n */\n\n function WebGLUtils( gl, extensions ) {\n\n function convert( p ) {\n\n var extension;\n\n if ( p === RepeatWrapping ) return gl.REPEAT;\n if ( p === ClampToEdgeWrapping ) return gl.CLAMP_TO_EDGE;\n if ( p === MirroredRepeatWrapping ) return gl.MIRRORED_REPEAT;\n\n if ( p === NearestFilter ) return gl.NEAREST;\n if ( p === NearestMipMapNearestFilter ) return gl.NEAREST_MIPMAP_NEAREST;\n if ( p === NearestMipMapLinearFilter ) return gl.NEAREST_MIPMAP_LINEAR;\n\n if ( p === LinearFilter ) return gl.LINEAR;\n if ( p === LinearMipMapNearestFilter ) return gl.LINEAR_MIPMAP_NEAREST;\n if ( p === LinearMipMapLinearFilter ) return gl.LINEAR_MIPMAP_LINEAR;\n\n if ( p === UnsignedByteType ) return gl.UNSIGNED_BYTE;\n if ( p === UnsignedShort4444Type ) return gl.UNSIGNED_SHORT_4_4_4_4;\n if ( p === UnsignedShort5551Type ) return gl.UNSIGNED_SHORT_5_5_5_1;\n if ( p === UnsignedShort565Type ) return gl.UNSIGNED_SHORT_5_6_5;\n\n if ( p === ByteType ) return gl.BYTE;\n if ( p === ShortType ) return gl.SHORT;\n if ( p === UnsignedShortType ) return gl.UNSIGNED_SHORT;\n if ( p === IntType ) return gl.INT;\n if ( p === UnsignedIntType ) return gl.UNSIGNED_INT;\n if ( p === FloatType ) return gl.FLOAT;\n\n if ( p === HalfFloatType ) {\n\n extension = extensions.get( 'OES_texture_half_float' );\n\n if ( extension !== null ) return extension.HALF_FLOAT_OES;\n\n }\n\n if ( p === AlphaFormat ) return gl.ALPHA;\n if ( p === RGBFormat ) return gl.RGB;\n if ( p === RGBAFormat ) return gl.RGBA;\n if ( p === LuminanceFormat ) return gl.LUMINANCE;\n if ( p === LuminanceAlphaFormat ) return gl.LUMINANCE_ALPHA;\n if ( p === DepthFormat ) return gl.DEPTH_COMPONENT;\n if ( p === DepthStencilFormat ) return gl.DEPTH_STENCIL;\n\n if ( p === AddEquation ) return gl.FUNC_ADD;\n if ( p === SubtractEquation ) return gl.FUNC_SUBTRACT;\n if ( p === ReverseSubtractEquation ) return gl.FUNC_REVERSE_SUBTRACT;\n\n if ( p === ZeroFactor ) return gl.ZERO;\n if ( p === OneFactor ) return gl.ONE;\n if ( p === SrcColorFactor ) return gl.SRC_COLOR;\n if ( p === OneMinusSrcColorFactor ) return gl.ONE_MINUS_SRC_COLOR;\n if ( p === SrcAlphaFactor ) return gl.SRC_ALPHA;\n if ( p === OneMinusSrcAlphaFactor ) return gl.ONE_MINUS_SRC_ALPHA;\n if ( p === DstAlphaFactor ) return gl.DST_ALPHA;\n if ( p === OneMinusDstAlphaFactor ) return gl.ONE_MINUS_DST_ALPHA;\n\n if ( p === DstColorFactor ) return gl.DST_COLOR;\n if ( p === OneMinusDstColorFactor ) return gl.ONE_MINUS_DST_COLOR;\n if ( p === SrcAlphaSaturateFactor ) return gl.SRC_ALPHA_SATURATE;\n\n if ( p === RGB_S3TC_DXT1_Format || p === RGBA_S3TC_DXT1_Format ||\n p === RGBA_S3TC_DXT3_Format || p === RGBA_S3TC_DXT5_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n if ( extension !== null ) {\n\n if ( p === RGB_S3TC_DXT1_Format ) return extension.COMPRESSED_RGB_S3TC_DXT1_EXT;\n if ( p === RGBA_S3TC_DXT1_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT1_EXT;\n if ( p === RGBA_S3TC_DXT3_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT3_EXT;\n if ( p === RGBA_S3TC_DXT5_Format ) return extension.COMPRESSED_RGBA_S3TC_DXT5_EXT;\n\n }\n\n }\n\n if ( p === RGB_PVRTC_4BPPV1_Format || p === RGB_PVRTC_2BPPV1_Format ||\n p === RGBA_PVRTC_4BPPV1_Format || p === RGBA_PVRTC_2BPPV1_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n if ( extension !== null ) {\n\n if ( p === RGB_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;\n if ( p === RGB_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;\n if ( p === RGBA_PVRTC_4BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;\n if ( p === RGBA_PVRTC_2BPPV1_Format ) return extension.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;\n\n }\n\n }\n\n if ( p === RGB_ETC1_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_etc1' );\n\n if ( extension !== null ) return extension.COMPRESSED_RGB_ETC1_WEBGL;\n\n }\n\n if ( p === RGBA_ASTC_4x4_Format || p === RGBA_ASTC_5x4_Format || p === RGBA_ASTC_5x5_Format ||\n p === RGBA_ASTC_6x5_Format || p === RGBA_ASTC_6x6_Format || p === RGBA_ASTC_8x5_Format ||\n p === RGBA_ASTC_8x6_Format || p === RGBA_ASTC_8x8_Format || p === RGBA_ASTC_10x5_Format ||\n p === RGBA_ASTC_10x6_Format || p === RGBA_ASTC_10x8_Format || p === RGBA_ASTC_10x10_Format ||\n p === RGBA_ASTC_12x10_Format || p === RGBA_ASTC_12x12_Format ) {\n\n extension = extensions.get( 'WEBGL_compressed_texture_astc' );\n\n if ( extension !== null ) {\n\n return p;\n\n }\n\n }\n\n if ( p === MinEquation || p === MaxEquation ) {\n\n extension = extensions.get( 'EXT_blend_minmax' );\n\n if ( extension !== null ) {\n\n if ( p === MinEquation ) return extension.MIN_EXT;\n if ( p === MaxEquation ) return extension.MAX_EXT;\n\n }\n\n }\n\n if ( p === UnsignedInt248Type ) {\n\n extension = extensions.get( 'WEBGL_depth_texture' );\n\n if ( extension !== null ) return extension.UNSIGNED_INT_24_8_WEBGL;\n\n }\n\n return 0;\n\n }\n\n return { convert: convert };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author greggman / http://games.greggman.com/\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author tschw\n */\n\n function PerspectiveCamera( fov, aspect, near, far ) {\n\n Camera.call( this );\n\n this.type = 'PerspectiveCamera';\n\n this.fov = fov !== undefined ? fov : 50;\n this.zoom = 1;\n\n this.near = near !== undefined ? near : 0.1;\n this.far = far !== undefined ? far : 2000;\n this.focus = 10;\n\n this.aspect = aspect !== undefined ? aspect : 1;\n this.view = null;\n\n this.filmGauge = 35; // width of the film (default in millimeters)\n this.filmOffset = 0; // horizontal film offset (same unit as gauge)\n\n this.updateProjectionMatrix();\n\n }\n\n PerspectiveCamera.prototype = Object.assign( Object.create( Camera.prototype ), {\n\n constructor: PerspectiveCamera,\n\n isPerspectiveCamera: true,\n\n copy: function ( source, recursive ) {\n\n Camera.prototype.copy.call( this, source, recursive );\n\n this.fov = source.fov;\n this.zoom = source.zoom;\n\n this.near = source.near;\n this.far = source.far;\n this.focus = source.focus;\n\n this.aspect = source.aspect;\n this.view = source.view === null ? null : Object.assign( {}, source.view );\n\n this.filmGauge = source.filmGauge;\n this.filmOffset = source.filmOffset;\n\n return this;\n\n },\n\n /**\n * Sets the FOV by focal length in respect to the current .filmGauge.\n *\n * The default film gauge is 35, so that the focal length can be specified for\n * a 35mm (full frame) camera.\n *\n * Values for focal length and film gauge must have the same unit.\n */\n setFocalLength: function ( focalLength ) {\n\n // see http://www.bobatkins.com/photography/technical/field_of_view.html\n var vExtentSlope = 0.5 * this.getFilmHeight() / focalLength;\n\n this.fov = _Math.RAD2DEG * 2 * Math.atan( vExtentSlope );\n this.updateProjectionMatrix();\n\n },\n\n /**\n * Calculates the focal length from the current .fov and .filmGauge.\n */\n getFocalLength: function () {\n\n var vExtentSlope = Math.tan( _Math.DEG2RAD * 0.5 * this.fov );\n\n return 0.5 * this.getFilmHeight() / vExtentSlope;\n\n },\n\n getEffectiveFOV: function () {\n\n return _Math.RAD2DEG * 2 * Math.atan(\n Math.tan( _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom );\n\n },\n\n getFilmWidth: function () {\n\n // film not completely covered in portrait format (aspect < 1)\n return this.filmGauge * Math.min( this.aspect, 1 );\n\n },\n\n getFilmHeight: function () {\n\n // film not completely covered in landscape format (aspect > 1)\n return this.filmGauge / Math.max( this.aspect, 1 );\n\n },\n\n /**\n * Sets an offset in a larger frustum. This is useful for multi-window or\n * multi-monitor/multi-machine setups.\n *\n * For example, if you have 3x2 monitors and each monitor is 1920x1080 and\n * the monitors are in grid like this\n *\n * +---+---+---+\n * | A | B | C |\n * +---+---+---+\n * | D | E | F |\n * +---+---+---+\n *\n * then for each monitor you would call it like this\n *\n * var w = 1920;\n * var h = 1080;\n * var fullWidth = w * 3;\n * var fullHeight = h * 2;\n *\n * --A--\n * camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h );\n * --B--\n * camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h );\n * --C--\n * camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h );\n * --D--\n * camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h );\n * --E--\n * camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h );\n * --F--\n * camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h );\n *\n * Note there is no reason monitors have to be the same size or in a grid.\n */\n setViewOffset: function ( fullWidth, fullHeight, x, y, width, height ) {\n\n this.aspect = fullWidth / fullHeight;\n\n if ( this.view === null ) {\n\n this.view = {\n enabled: true,\n fullWidth: 1,\n fullHeight: 1,\n offsetX: 0,\n offsetY: 0,\n width: 1,\n height: 1\n };\n\n }\n\n this.view.enabled = true;\n this.view.fullWidth = fullWidth;\n this.view.fullHeight = fullHeight;\n this.view.offsetX = x;\n this.view.offsetY = y;\n this.view.width = width;\n this.view.height = height;\n\n this.updateProjectionMatrix();\n\n },\n\n clearViewOffset: function () {\n\n if ( this.view !== null ) {\n\n this.view.enabled = false;\n\n }\n\n this.updateProjectionMatrix();\n\n },\n\n updateProjectionMatrix: function () {\n\n var near = this.near,\n top = near * Math.tan(\n _Math.DEG2RAD * 0.5 * this.fov ) / this.zoom,\n height = 2 * top,\n width = this.aspect * height,\n left = - 0.5 * width,\n view = this.view;\n\n if ( this.view !== null && this.view.enabled ) {\n\n var fullWidth = view.fullWidth,\n fullHeight = view.fullHeight;\n\n left += view.offsetX * width / fullWidth;\n top -= view.offsetY * height / fullHeight;\n width *= view.width / fullWidth;\n height *= view.height / fullHeight;\n\n }\n\n var skew = this.filmOffset;\n if ( skew !== 0 ) left += near * skew / this.getFilmWidth();\n\n this.projectionMatrix.makePerspective( left, left + width, top, top - height, near, this.far );\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.fov = this.fov;\n data.object.zoom = this.zoom;\n\n data.object.near = this.near;\n data.object.far = this.far;\n data.object.focus = this.focus;\n\n data.object.aspect = this.aspect;\n\n if ( this.view !== null ) data.object.view = Object.assign( {}, this.view );\n\n data.object.filmGauge = this.filmGauge;\n data.object.filmOffset = this.filmOffset;\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ArrayCamera( array ) {\n\n PerspectiveCamera.call( this );\n\n this.cameras = array || [];\n\n }\n\n ArrayCamera.prototype = Object.assign( Object.create( PerspectiveCamera.prototype ), {\n\n constructor: ArrayCamera,\n\n isArrayCamera: true\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function WebVRManager( renderer ) {\n\n var scope = this;\n\n var device = null;\n var frameData = null;\n\n var poseTarget = null;\n\n var standingMatrix = new Matrix4();\n var standingMatrixInverse = new Matrix4();\n\n if ( typeof window !== 'undefined' && 'VRFrameData' in window ) {\n\n frameData = new window.VRFrameData();\n\n }\n\n var matrixWorldInverse = new Matrix4();\n var tempQuaternion = new Quaternion();\n var tempPosition = new Vector3();\n\n var cameraL = new PerspectiveCamera();\n cameraL.bounds = new Vector4( 0.0, 0.0, 0.5, 1.0 );\n cameraL.layers.enable( 1 );\n\n var cameraR = new PerspectiveCamera();\n cameraR.bounds = new Vector4( 0.5, 0.0, 0.5, 1.0 );\n cameraR.layers.enable( 2 );\n\n var cameraVR = new ArrayCamera( [ cameraL, cameraR ] );\n cameraVR.layers.enable( 1 );\n cameraVR.layers.enable( 2 );\n\n //\n\n var currentSize, currentPixelRatio;\n\n function onVRDisplayPresentChange() {\n\n if ( device !== null && device.isPresenting ) {\n\n var eyeParameters = device.getEyeParameters( 'left' );\n var renderWidth = eyeParameters.renderWidth;\n var renderHeight = eyeParameters.renderHeight;\n\n currentPixelRatio = renderer.getPixelRatio();\n currentSize = renderer.getSize();\n\n renderer.setDrawingBufferSize( renderWidth * 2, renderHeight, 1 );\n\n } else if ( scope.enabled ) {\n\n renderer.setDrawingBufferSize( currentSize.width, currentSize.height, currentPixelRatio );\n\n }\n\n }\n\n if ( typeof window !== 'undefined' ) {\n\n window.addEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange, false );\n\n }\n\n //\n\n this.enabled = false;\n this.userHeight = 1.6;\n\n this.getDevice = function () {\n\n return device;\n\n };\n\n this.setDevice = function ( value ) {\n\n if ( value !== undefined ) device = value;\n\n };\n\n this.setPoseTarget = function ( object ) {\n\n if ( object !== undefined ) poseTarget = object;\n\n };\n\n this.getCamera = function ( camera ) {\n\n if ( device === null ) return camera;\n\n device.depthNear = camera.near;\n device.depthFar = camera.far;\n\n device.getFrameData( frameData );\n\n //\n\n var stageParameters = device.stageParameters;\n\n if ( stageParameters ) {\n\n standingMatrix.fromArray( stageParameters.sittingToStandingTransform );\n\n } else {\n\n standingMatrix.makeTranslation( 0, scope.userHeight, 0 );\n\n }\n\n\n var pose = frameData.pose;\n var poseObject = poseTarget !== null ? poseTarget : camera;\n\n // We want to manipulate poseObject by its position and quaternion components since users may rely on them.\n poseObject.matrix.copy( standingMatrix );\n poseObject.matrix.decompose( poseObject.position, poseObject.quaternion, poseObject.scale );\n\n if ( pose.orientation !== null ) {\n\n tempQuaternion.fromArray( pose.orientation );\n poseObject.quaternion.multiply( tempQuaternion );\n\n }\n\n if ( pose.position !== null ) {\n\n tempQuaternion.setFromRotationMatrix( standingMatrix );\n tempPosition.fromArray( pose.position );\n tempPosition.applyQuaternion( tempQuaternion );\n poseObject.position.add( tempPosition );\n\n }\n\n poseObject.updateMatrixWorld();\n\n if ( device.isPresenting === false ) return camera;\n\n //\n\n cameraL.near = camera.near;\n cameraR.near = camera.near;\n\n cameraL.far = camera.far;\n cameraR.far = camera.far;\n\n cameraVR.matrixWorld.copy( camera.matrixWorld );\n cameraVR.matrixWorldInverse.copy( camera.matrixWorldInverse );\n\n cameraL.matrixWorldInverse.fromArray( frameData.leftViewMatrix );\n cameraR.matrixWorldInverse.fromArray( frameData.rightViewMatrix );\n\n // TODO (mrdoob) Double check this code\n\n standingMatrixInverse.getInverse( standingMatrix );\n\n cameraL.matrixWorldInverse.multiply( standingMatrixInverse );\n cameraR.matrixWorldInverse.multiply( standingMatrixInverse );\n\n var parent = poseObject.parent;\n\n if ( parent !== null ) {\n\n matrixWorldInverse.getInverse( parent.matrixWorld );\n\n cameraL.matrixWorldInverse.multiply( matrixWorldInverse );\n cameraR.matrixWorldInverse.multiply( matrixWorldInverse );\n\n }\n\n // envMap and Mirror needs camera.matrixWorld\n\n cameraL.matrixWorld.getInverse( cameraL.matrixWorldInverse );\n cameraR.matrixWorld.getInverse( cameraR.matrixWorldInverse );\n\n cameraL.projectionMatrix.fromArray( frameData.leftProjectionMatrix );\n cameraR.projectionMatrix.fromArray( frameData.rightProjectionMatrix );\n\n // HACK (mrdoob)\n // https://github.com/w3c/webvr/issues/203\n\n cameraVR.projectionMatrix.copy( cameraL.projectionMatrix );\n\n //\n\n var layers = device.getLayers();\n\n if ( layers.length ) {\n\n var layer = layers[ 0 ];\n\n if ( layer.leftBounds !== null && layer.leftBounds.length === 4 ) {\n\n cameraL.bounds.fromArray( layer.leftBounds );\n\n }\n\n if ( layer.rightBounds !== null && layer.rightBounds.length === 4 ) {\n\n cameraR.bounds.fromArray( layer.rightBounds );\n\n }\n\n }\n\n return cameraVR;\n\n };\n\n this.getStandingMatrix = function () {\n\n return standingMatrix;\n\n };\n\n this.submitFrame = function () {\n\n if ( device && device.isPresenting ) device.submitFrame();\n\n };\n\n this.dispose = function () {\n\n if ( typeof window !== 'undefined' ) {\n\n window.removeEventListener( 'vrdisplaypresentchange', onVRDisplayPresentChange );\n\n }\n\n };\n\n }\n\n /**\n * @author supereggbert / http://www.paulbrunt.co.uk/\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n * @author szimek / https://github.com/szimek/\n * @author tschw\n */\n\n function WebGLRenderer( parameters ) {\n\n console.log( 'THREE.WebGLRenderer', REVISION );\n\n parameters = parameters || {};\n\n var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' ),\n _context = parameters.context !== undefined ? parameters.context : null,\n\n _alpha = parameters.alpha !== undefined ? parameters.alpha : false,\n _depth = parameters.depth !== undefined ? parameters.depth : true,\n _stencil = parameters.stencil !== undefined ? parameters.stencil : true,\n _antialias = parameters.antialias !== undefined ? parameters.antialias : false,\n _premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true,\n _preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false,\n _powerPreference = parameters.powerPreference !== undefined ? parameters.powerPreference : 'default';\n\n var currentRenderList = null;\n var currentRenderState = null;\n\n // public properties\n\n this.domElement = _canvas;\n this.context = null;\n\n // clearing\n\n this.autoClear = true;\n this.autoClearColor = true;\n this.autoClearDepth = true;\n this.autoClearStencil = true;\n\n // scene graph\n\n this.sortObjects = true;\n\n // user-defined clipping\n\n this.clippingPlanes = [];\n this.localClippingEnabled = false;\n\n // physically based shading\n\n this.gammaFactor = 2.0; // for backwards compatibility\n this.gammaInput = false;\n this.gammaOutput = false;\n\n // physical lights\n\n this.physicallyCorrectLights = false;\n\n // tone mapping\n\n this.toneMapping = LinearToneMapping;\n this.toneMappingExposure = 1.0;\n this.toneMappingWhitePoint = 1.0;\n\n // morphs\n\n this.maxMorphTargets = 8;\n this.maxMorphNormals = 4;\n\n // internal properties\n\n var _this = this,\n\n _isContextLost = false,\n\n // internal state cache\n\n _currentRenderTarget = null,\n _currentFramebuffer = null,\n _currentMaterialId = - 1,\n _currentGeometryProgram = '',\n\n _currentCamera = null,\n _currentArrayCamera = null,\n\n _currentViewport = new Vector4(),\n _currentScissor = new Vector4(),\n _currentScissorTest = null,\n\n //\n\n _usedTextureUnits = 0,\n\n //\n\n _width = _canvas.width,\n _height = _canvas.height,\n\n _pixelRatio = 1,\n\n _viewport = new Vector4( 0, 0, _width, _height ),\n _scissor = new Vector4( 0, 0, _width, _height ),\n _scissorTest = false,\n\n // frustum\n\n _frustum = new Frustum(),\n\n // clipping\n\n _clipping = new WebGLClipping(),\n _clippingEnabled = false,\n _localClippingEnabled = false,\n\n // camera matrices cache\n\n _projScreenMatrix = new Matrix4(),\n\n _vector3 = new Vector3();\n\n function getTargetPixelRatio() {\n\n return _currentRenderTarget === null ? _pixelRatio : 1;\n\n }\n\n // initialize\n\n var _gl;\n\n try {\n\n var contextAttributes = {\n alpha: _alpha,\n depth: _depth,\n stencil: _stencil,\n antialias: _antialias,\n premultipliedAlpha: _premultipliedAlpha,\n preserveDrawingBuffer: _preserveDrawingBuffer,\n powerPreference: _powerPreference\n };\n\n // event listeners must be registered before WebGL context is created, see #12753\n\n _canvas.addEventListener( 'webglcontextlost', onContextLost, false );\n _canvas.addEventListener( 'webglcontextrestored', onContextRestore, false );\n\n _gl = _context || _canvas.getContext( 'webgl', contextAttributes ) || _canvas.getContext( 'experimental-webgl', contextAttributes );\n\n if ( _gl === null ) {\n\n if ( _canvas.getContext( 'webgl' ) !== null ) {\n\n throw new Error( 'Error creating WebGL context with your selected attributes.' );\n\n } else {\n\n throw new Error( 'Error creating WebGL context.' );\n\n }\n\n }\n\n // Some experimental-webgl implementations do not have getShaderPrecisionFormat\n\n if ( _gl.getShaderPrecisionFormat === undefined ) {\n\n _gl.getShaderPrecisionFormat = function () {\n\n return { 'rangeMin': 1, 'rangeMax': 1, 'precision': 1 };\n\n };\n\n }\n\n } catch ( error ) {\n\n console.error( 'THREE.WebGLRenderer: ' + error.message );\n\n }\n\n var extensions, capabilities, state, info;\n var properties, textures, attributes, geometries, objects;\n var programCache, renderLists, renderStates;\n\n var background, morphtargets, bufferRenderer, indexedBufferRenderer;\n var spriteRenderer;\n\n var utils;\n\n function initGLContext() {\n\n extensions = new WebGLExtensions( _gl );\n extensions.get( 'WEBGL_depth_texture' );\n extensions.get( 'OES_texture_float' );\n extensions.get( 'OES_texture_float_linear' );\n extensions.get( 'OES_texture_half_float' );\n extensions.get( 'OES_texture_half_float_linear' );\n extensions.get( 'OES_standard_derivatives' );\n extensions.get( 'OES_element_index_uint' );\n extensions.get( 'ANGLE_instanced_arrays' );\n\n utils = new WebGLUtils( _gl, extensions );\n\n capabilities = new WebGLCapabilities( _gl, extensions, parameters );\n\n state = new WebGLState( _gl, extensions, utils );\n state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );\n state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );\n\n info = new WebGLInfo( _gl );\n properties = new WebGLProperties();\n textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );\n attributes = new WebGLAttributes( _gl );\n geometries = new WebGLGeometries( _gl, attributes, info );\n objects = new WebGLObjects( geometries, info );\n morphtargets = new WebGLMorphtargets( _gl );\n programCache = new WebGLPrograms( _this, extensions, capabilities );\n renderLists = new WebGLRenderLists();\n renderStates = new WebGLRenderStates();\n\n background = new WebGLBackground( _this, state, geometries, _premultipliedAlpha );\n\n bufferRenderer = new WebGLBufferRenderer( _gl, extensions, info );\n indexedBufferRenderer = new WebGLIndexedBufferRenderer( _gl, extensions, info );\n\n spriteRenderer = new WebGLSpriteRenderer( _this, _gl, state, textures, capabilities );\n\n info.programs = programCache.programs;\n\n _this.context = _gl;\n _this.capabilities = capabilities;\n _this.extensions = extensions;\n _this.properties = properties;\n _this.renderLists = renderLists;\n _this.state = state;\n _this.info = info;\n\n }\n\n initGLContext();\n\n // vr\n\n var vr = new WebVRManager( _this );\n\n this.vr = vr;\n\n // shadow map\n\n var shadowMap = new WebGLShadowMap( _this, objects, capabilities.maxTextureSize );\n\n this.shadowMap = shadowMap;\n\n // API\n\n this.getContext = function () {\n\n return _gl;\n\n };\n\n this.getContextAttributes = function () {\n\n return _gl.getContextAttributes();\n\n };\n\n this.forceContextLoss = function () {\n\n var extension = extensions.get( 'WEBGL_lose_context' );\n if ( extension ) extension.loseContext();\n\n };\n\n this.forceContextRestore = function () {\n\n var extension = extensions.get( 'WEBGL_lose_context' );\n if ( extension ) extension.restoreContext();\n\n };\n\n this.getPixelRatio = function () {\n\n return _pixelRatio;\n\n };\n\n this.setPixelRatio = function ( value ) {\n\n if ( value === undefined ) return;\n\n _pixelRatio = value;\n\n this.setSize( _width, _height, false );\n\n };\n\n this.getSize = function () {\n\n return {\n width: _width,\n height: _height\n };\n\n };\n\n this.setSize = function ( width, height, updateStyle ) {\n\n var device = vr.getDevice();\n\n if ( device && device.isPresenting ) {\n\n console.warn( 'THREE.WebGLRenderer: Can\\'t change size while VR device is presenting.' );\n return;\n\n }\n\n _width = width;\n _height = height;\n\n _canvas.width = width * _pixelRatio;\n _canvas.height = height * _pixelRatio;\n\n if ( updateStyle !== false ) {\n\n _canvas.style.width = width + 'px';\n _canvas.style.height = height + 'px';\n\n }\n\n this.setViewport( 0, 0, width, height );\n\n };\n\n this.getDrawingBufferSize = function () {\n\n return {\n width: _width * _pixelRatio,\n height: _height * _pixelRatio\n };\n\n };\n\n this.setDrawingBufferSize = function ( width, height, pixelRatio ) {\n\n _width = width;\n _height = height;\n\n _pixelRatio = pixelRatio;\n\n _canvas.width = width * pixelRatio;\n _canvas.height = height * pixelRatio;\n\n this.setViewport( 0, 0, width, height );\n\n };\n\n this.getCurrentViewport = function () {\n\n return _currentViewport;\n\n };\n\n this.setViewport = function ( x, y, width, height ) {\n\n _viewport.set( x, _height - y - height, width, height );\n state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) );\n\n };\n\n this.setScissor = function ( x, y, width, height ) {\n\n _scissor.set( x, _height - y - height, width, height );\n state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) );\n\n };\n\n this.setScissorTest = function ( boolean ) {\n\n state.setScissorTest( _scissorTest = boolean );\n\n };\n\n // Clearing\n\n this.getClearColor = function () {\n\n return background.getClearColor();\n\n };\n\n this.setClearColor = function () {\n\n background.setClearColor.apply( background, arguments );\n\n };\n\n this.getClearAlpha = function () {\n\n return background.getClearAlpha();\n\n };\n\n this.setClearAlpha = function () {\n\n background.setClearAlpha.apply( background, arguments );\n\n };\n\n this.clear = function ( color, depth, stencil ) {\n\n var bits = 0;\n\n if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT;\n if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT;\n if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT;\n\n _gl.clear( bits );\n\n };\n\n this.clearColor = function () {\n\n this.clear( true, false, false );\n\n };\n\n this.clearDepth = function () {\n\n this.clear( false, true, false );\n\n };\n\n this.clearStencil = function () {\n\n this.clear( false, false, true );\n\n };\n\n this.clearTarget = function ( renderTarget, color, depth, stencil ) {\n\n this.setRenderTarget( renderTarget );\n this.clear( color, depth, stencil );\n\n };\n\n //\n\n this.dispose = function () {\n\n _canvas.removeEventListener( 'webglcontextlost', onContextLost, false );\n _canvas.removeEventListener( 'webglcontextrestored', onContextRestore, false );\n\n renderLists.dispose();\n renderStates.dispose();\n properties.dispose();\n objects.dispose();\n\n vr.dispose();\n\n stopAnimation();\n\n };\n\n // Events\n\n function onContextLost( event ) {\n\n event.preventDefault();\n\n console.log( 'THREE.WebGLRenderer: Context Lost.' );\n\n _isContextLost = true;\n\n }\n\n function onContextRestore( /* event */ ) {\n\n console.log( 'THREE.WebGLRenderer: Context Restored.' );\n\n _isContextLost = false;\n\n initGLContext();\n\n }\n\n function onMaterialDispose( event ) {\n\n var material = event.target;\n\n material.removeEventListener( 'dispose', onMaterialDispose );\n\n deallocateMaterial( material );\n\n }\n\n // Buffer deallocation\n\n function deallocateMaterial( material ) {\n\n releaseMaterialProgramReference( material );\n\n properties.remove( material );\n\n }\n\n\n function releaseMaterialProgramReference( material ) {\n\n var programInfo = properties.get( material ).program;\n\n material.program = undefined;\n\n if ( programInfo !== undefined ) {\n\n programCache.releaseProgram( programInfo );\n\n }\n\n }\n\n // Buffer rendering\n\n function renderObjectImmediate( object, program, material ) {\n\n object.render( function ( object ) {\n\n _this.renderBufferImmediate( object, program, material );\n\n } );\n\n }\n\n this.renderBufferImmediate = function ( object, program, material ) {\n\n state.initAttributes();\n\n var buffers = properties.get( object );\n\n if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer();\n if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer();\n if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer();\n if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer();\n\n var programAttributes = program.getAttributes();\n\n if ( object.hasPositions ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.position );\n _gl.vertexAttribPointer( programAttributes.position, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasNormals ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal );\n\n if ( ! material.isMeshPhongMaterial &&\n ! material.isMeshStandardMaterial &&\n ! material.isMeshNormalMaterial &&\n material.flatShading === true ) {\n\n for ( var i = 0, l = object.count * 3; i < l; i += 9 ) {\n\n var array = object.normalArray;\n\n var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3;\n var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3;\n var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3;\n\n array[ i + 0 ] = nx;\n array[ i + 1 ] = ny;\n array[ i + 2 ] = nz;\n\n array[ i + 3 ] = nx;\n array[ i + 4 ] = ny;\n array[ i + 5 ] = nz;\n\n array[ i + 6 ] = nx;\n array[ i + 7 ] = ny;\n array[ i + 8 ] = nz;\n\n }\n\n }\n\n _gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.normal );\n\n _gl.vertexAttribPointer( programAttributes.normal, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasUvs && material.map ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.uv );\n\n _gl.vertexAttribPointer( programAttributes.uv, 2, _gl.FLOAT, false, 0, 0 );\n\n }\n\n if ( object.hasColors && material.vertexColors !== NoColors ) {\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color );\n _gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW );\n\n state.enableAttribute( programAttributes.color );\n\n _gl.vertexAttribPointer( programAttributes.color, 3, _gl.FLOAT, false, 0, 0 );\n\n }\n\n state.disableUnusedAttributes();\n\n _gl.drawArrays( _gl.TRIANGLES, 0, object.count );\n\n object.count = 0;\n\n };\n\n this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) {\n\n var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n state.setMaterial( material, frontFaceCW );\n\n var program = setProgram( camera, fog, material, object );\n var geometryProgram = geometry.id + '_' + program.id + '_' + ( material.wireframe === true );\n\n var updateBuffers = false;\n\n if ( geometryProgram !== _currentGeometryProgram ) {\n\n _currentGeometryProgram = geometryProgram;\n updateBuffers = true;\n\n }\n\n if ( object.morphTargetInfluences ) {\n\n morphtargets.update( object, geometry, material, program );\n\n updateBuffers = true;\n\n }\n\n //\n\n var index = geometry.index;\n var position = geometry.attributes.position;\n var rangeFactor = 1;\n\n if ( material.wireframe === true ) {\n\n index = geometries.getWireframeAttribute( geometry );\n rangeFactor = 2;\n\n }\n\n var attribute;\n var renderer = bufferRenderer;\n\n if ( index !== null ) {\n\n attribute = attributes.get( index );\n\n renderer = indexedBufferRenderer;\n renderer.setIndex( attribute );\n\n }\n\n if ( updateBuffers ) {\n\n setupVertexAttributes( material, program, geometry );\n\n if ( index !== null ) {\n\n _gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, attribute.buffer );\n\n }\n\n }\n\n //\n\n var dataCount = Infinity;\n\n if ( index !== null ) {\n\n dataCount = index.count;\n\n } else if ( position !== undefined ) {\n\n dataCount = position.count;\n\n }\n\n var rangeStart = geometry.drawRange.start * rangeFactor;\n var rangeCount = geometry.drawRange.count * rangeFactor;\n\n var groupStart = group !== null ? group.start * rangeFactor : 0;\n var groupCount = group !== null ? group.count * rangeFactor : Infinity;\n\n var drawStart = Math.max( rangeStart, groupStart );\n var drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;\n\n var drawCount = Math.max( 0, drawEnd - drawStart + 1 );\n\n if ( drawCount === 0 ) return;\n\n //\n\n if ( object.isMesh ) {\n\n if ( material.wireframe === true ) {\n\n state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );\n renderer.setMode( _gl.LINES );\n\n } else {\n\n switch ( object.drawMode ) {\n\n case TrianglesDrawMode:\n renderer.setMode( _gl.TRIANGLES );\n break;\n\n case TriangleStripDrawMode:\n renderer.setMode( _gl.TRIANGLE_STRIP );\n break;\n\n case TriangleFanDrawMode:\n renderer.setMode( _gl.TRIANGLE_FAN );\n break;\n\n }\n\n }\n\n\n } else if ( object.isLine ) {\n\n var lineWidth = material.linewidth;\n\n if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material\n\n state.setLineWidth( lineWidth * getTargetPixelRatio() );\n\n if ( object.isLineSegments ) {\n\n renderer.setMode( _gl.LINES );\n\n } else if ( object.isLineLoop ) {\n\n renderer.setMode( _gl.LINE_LOOP );\n\n } else {\n\n renderer.setMode( _gl.LINE_STRIP );\n\n }\n\n } else if ( object.isPoints ) {\n\n renderer.setMode( _gl.POINTS );\n\n }\n\n if ( geometry && geometry.isInstancedBufferGeometry ) {\n\n if ( geometry.maxInstancedCount > 0 ) {\n\n renderer.renderInstances( geometry, drawStart, drawCount );\n\n }\n\n } else {\n\n renderer.render( drawStart, drawCount );\n\n }\n\n };\n\n function setupVertexAttributes( material, program, geometry, startIndex ) {\n\n if ( geometry && geometry.isInstancedBufferGeometry ) {\n\n if ( extensions.get( 'ANGLE_instanced_arrays' ) === null ) {\n\n console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hardware does not support extension ANGLE_instanced_arrays.' );\n return;\n\n }\n\n }\n\n if ( startIndex === undefined ) startIndex = 0;\n\n state.initAttributes();\n\n var geometryAttributes = geometry.attributes;\n\n var programAttributes = program.getAttributes();\n\n var materialDefaultAttributeValues = material.defaultAttributeValues;\n\n for ( var name in programAttributes ) {\n\n var programAttribute = programAttributes[ name ];\n\n if ( programAttribute >= 0 ) {\n\n var geometryAttribute = geometryAttributes[ name ];\n\n if ( geometryAttribute !== undefined ) {\n\n var normalized = geometryAttribute.normalized;\n var size = geometryAttribute.itemSize;\n\n var attribute = attributes.get( geometryAttribute );\n\n // TODO Attribute may not be available on context restore\n\n if ( attribute === undefined ) continue;\n\n var buffer = attribute.buffer;\n var type = attribute.type;\n var bytesPerElement = attribute.bytesPerElement;\n\n if ( geometryAttribute.isInterleavedBufferAttribute ) {\n\n var data = geometryAttribute.data;\n var stride = data.stride;\n var offset = geometryAttribute.offset;\n\n if ( data && data.isInstancedInterleavedBuffer ) {\n\n state.enableAttributeAndDivisor( programAttribute, data.meshPerAttribute );\n\n if ( geometry.maxInstancedCount === undefined ) {\n\n geometry.maxInstancedCount = data.meshPerAttribute * data.count;\n\n }\n\n } else {\n\n state.enableAttribute( programAttribute );\n\n }\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n _gl.vertexAttribPointer( programAttribute, size, type, normalized, stride * bytesPerElement, ( startIndex * stride + offset ) * bytesPerElement );\n\n } else {\n\n if ( geometryAttribute.isInstancedBufferAttribute ) {\n\n state.enableAttributeAndDivisor( programAttribute, geometryAttribute.meshPerAttribute );\n\n if ( geometry.maxInstancedCount === undefined ) {\n\n geometry.maxInstancedCount = geometryAttribute.meshPerAttribute * geometryAttribute.count;\n\n }\n\n } else {\n\n state.enableAttribute( programAttribute );\n\n }\n\n _gl.bindBuffer( _gl.ARRAY_BUFFER, buffer );\n _gl.vertexAttribPointer( programAttribute, size, type, normalized, 0, startIndex * size * bytesPerElement );\n\n }\n\n } else if ( materialDefaultAttributeValues !== undefined ) {\n\n var value = materialDefaultAttributeValues[ name ];\n\n if ( value !== undefined ) {\n\n switch ( value.length ) {\n\n case 2:\n _gl.vertexAttrib2fv( programAttribute, value );\n break;\n\n case 3:\n _gl.vertexAttrib3fv( programAttribute, value );\n break;\n\n case 4:\n _gl.vertexAttrib4fv( programAttribute, value );\n break;\n\n default:\n _gl.vertexAttrib1fv( programAttribute, value );\n\n }\n\n }\n\n }\n\n }\n\n }\n\n state.disableUnusedAttributes();\n\n }\n\n // Compile\n\n this.compile = function ( scene, camera ) {\n\n currentRenderState = renderStates.get( scene, camera );\n currentRenderState.init();\n\n scene.traverse( function ( object ) {\n\n if ( object.isLight ) {\n\n currentRenderState.pushLight( object );\n\n if ( object.castShadow ) {\n\n currentRenderState.pushShadow( object );\n\n }\n\n }\n\n } );\n\n currentRenderState.setupLights( camera );\n\n scene.traverse( function ( object ) {\n\n if ( object.material ) {\n\n if ( Array.isArray( object.material ) ) {\n\n for ( var i = 0; i < object.material.length; i ++ ) {\n\n initMaterial( object.material[ i ], scene.fog, object );\n\n }\n\n } else {\n\n initMaterial( object.material, scene.fog, object );\n\n }\n\n }\n\n } );\n\n };\n\n // Animation Loop\n\n var isAnimating = false;\n var onAnimationFrame = null;\n\n function startAnimation() {\n\n if ( isAnimating ) return;\n\n requestAnimationLoopFrame();\n\n isAnimating = true;\n\n }\n\n function stopAnimation() {\n\n isAnimating = false;\n\n }\n\n function requestAnimationLoopFrame() {\n\n var device = vr.getDevice();\n\n if ( device && device.isPresenting ) {\n\n device.requestAnimationFrame( animationLoop );\n\n } else {\n\n window.requestAnimationFrame( animationLoop );\n\n }\n\n }\n\n function animationLoop( time ) {\n\n if ( isAnimating === false ) return;\n\n onAnimationFrame( time );\n\n requestAnimationLoopFrame();\n\n }\n\n this.animate = function ( callback ) {\n\n onAnimationFrame = callback;\n onAnimationFrame !== null ? startAnimation() : stopAnimation();\n\n };\n\n // Rendering\n\n this.render = function ( scene, camera, renderTarget, forceClear ) {\n\n if ( ! ( camera && camera.isCamera ) ) {\n\n console.error( 'THREE.WebGLRenderer.render: camera is not an instance of THREE.Camera.' );\n return;\n\n }\n\n if ( _isContextLost ) return;\n\n // reset caching for this frame\n\n _currentGeometryProgram = '';\n _currentMaterialId = - 1;\n _currentCamera = null;\n\n // update scene graph\n\n if ( scene.autoUpdate === true ) scene.updateMatrixWorld();\n\n // update camera matrices and frustum\n\n if ( camera.parent === null ) camera.updateMatrixWorld();\n\n if ( vr.enabled ) {\n\n camera = vr.getCamera( camera );\n\n }\n\n //\n\n currentRenderState = renderStates.get( scene, camera );\n currentRenderState.init();\n\n scene.onBeforeRender( _this, scene, camera, renderTarget );\n\n _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );\n _frustum.setFromMatrix( _projScreenMatrix );\n\n _localClippingEnabled = this.localClippingEnabled;\n _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );\n\n currentRenderList = renderLists.get( scene, camera );\n currentRenderList.init();\n\n projectObject( scene, camera, _this.sortObjects );\n\n if ( _this.sortObjects === true ) {\n\n currentRenderList.sort();\n\n }\n\n //\n\n if ( _clippingEnabled ) _clipping.beginShadows();\n\n var shadowsArray = currentRenderState.state.shadowsArray;\n\n shadowMap.render( shadowsArray, scene, camera );\n\n currentRenderState.setupLights( camera );\n\n if ( _clippingEnabled ) _clipping.endShadows();\n\n //\n\n if ( this.info.autoReset ) this.info.reset();\n\n if ( renderTarget === undefined ) {\n\n renderTarget = null;\n\n }\n\n this.setRenderTarget( renderTarget );\n\n //\n\n background.render( currentRenderList, scene, camera, forceClear );\n\n // render scene\n\n var opaqueObjects = currentRenderList.opaque;\n var transparentObjects = currentRenderList.transparent;\n\n if ( scene.overrideMaterial ) {\n\n var overrideMaterial = scene.overrideMaterial;\n\n if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );\n if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );\n\n } else {\n\n // opaque pass (front-to-back order)\n\n if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );\n\n // transparent pass (back-to-front order)\n\n if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );\n\n }\n\n // custom renderers\n\n var spritesArray = currentRenderState.state.spritesArray;\n\n spriteRenderer.render( spritesArray, scene, camera );\n\n // Generate mipmap if we're using any kind of mipmap filtering\n\n if ( renderTarget ) {\n\n textures.updateRenderTargetMipmap( renderTarget );\n\n }\n\n // Ensure depth buffer writing is enabled so it can be cleared on next render\n\n state.buffers.depth.setTest( true );\n state.buffers.depth.setMask( true );\n state.buffers.color.setMask( true );\n\n state.setPolygonOffset( false );\n\n scene.onAfterRender( _this, scene, camera );\n\n if ( vr.enabled ) {\n\n vr.submitFrame();\n\n }\n\n // _gl.finish();\n\n currentRenderList = null;\n currentRenderState = null;\n\n };\n\n /*\n // TODO Duplicated code (Frustum)\n\n var _sphere = new Sphere();\n\n function isObjectViewable( object ) {\n\n var geometry = object.geometry;\n\n if ( geometry.boundingSphere === null )\n geometry.computeBoundingSphere();\n\n _sphere.copy( geometry.boundingSphere ).\n applyMatrix4( object.matrixWorld );\n\n return isSphereViewable( _sphere );\n\n }\n\n function isSpriteViewable( sprite ) {\n\n _sphere.center.set( 0, 0, 0 );\n _sphere.radius = 0.7071067811865476;\n _sphere.applyMatrix4( sprite.matrixWorld );\n\n return isSphereViewable( _sphere );\n\n }\n\n function isSphereViewable( sphere ) {\n\n if ( ! _frustum.intersectsSphere( sphere ) ) return false;\n\n var numPlanes = _clipping.numPlanes;\n\n if ( numPlanes === 0 ) return true;\n\n var planes = _this.clippingPlanes,\n\n center = sphere.center,\n negRad = - sphere.radius,\n i = 0;\n\n do {\n\n // out when deeper than radius in the negative halfspace\n if ( planes[ i ].distanceToPoint( center ) < negRad ) return false;\n\n } while ( ++ i !== numPlanes );\n\n return true;\n\n }\n */\n\n function projectObject( object, camera, sortObjects ) {\n\n if ( object.visible === false ) return;\n\n var visible = object.layers.test( camera.layers );\n\n if ( visible ) {\n\n if ( object.isLight ) {\n\n currentRenderState.pushLight( object );\n\n if ( object.castShadow ) {\n\n currentRenderState.pushShadow( object );\n\n }\n\n } else if ( object.isSprite ) {\n\n if ( ! object.frustumCulled || _frustum.intersectsSprite( object ) ) {\n\n currentRenderState.pushSprite( object );\n\n }\n\n } else if ( object.isImmediateRenderObject ) {\n\n if ( sortObjects ) {\n\n _vector3.setFromMatrixPosition( object.matrixWorld )\n .applyMatrix4( _projScreenMatrix );\n\n }\n\n currentRenderList.push( object, null, object.material, _vector3.z, null );\n\n } else if ( object.isMesh || object.isLine || object.isPoints ) {\n\n if ( object.isSkinnedMesh ) {\n\n object.skeleton.update();\n\n }\n\n if ( ! object.frustumCulled || _frustum.intersectsObject( object ) ) {\n\n if ( sortObjects ) {\n\n _vector3.setFromMatrixPosition( object.matrixWorld )\n .applyMatrix4( _projScreenMatrix );\n\n }\n\n var geometry = objects.update( object );\n var material = object.material;\n\n if ( Array.isArray( material ) ) {\n\n var groups = geometry.groups;\n\n for ( var i = 0, l = groups.length; i < l; i ++ ) {\n\n var group = groups[ i ];\n var groupMaterial = material[ group.materialIndex ];\n\n if ( groupMaterial && groupMaterial.visible ) {\n\n currentRenderList.push( object, geometry, groupMaterial, _vector3.z, group );\n\n }\n\n }\n\n } else if ( material.visible ) {\n\n currentRenderList.push( object, geometry, material, _vector3.z, null );\n\n }\n\n }\n\n }\n\n }\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n projectObject( children[ i ], camera, sortObjects );\n\n }\n\n }\n\n function renderObjects( renderList, scene, camera, overrideMaterial ) {\n\n for ( var i = 0, l = renderList.length; i < l; i ++ ) {\n\n var renderItem = renderList[ i ];\n\n var object = renderItem.object;\n var geometry = renderItem.geometry;\n var material = overrideMaterial === undefined ? renderItem.material : overrideMaterial;\n var group = renderItem.group;\n\n if ( camera.isArrayCamera ) {\n\n _currentArrayCamera = camera;\n\n var cameras = camera.cameras;\n\n for ( var j = 0, jl = cameras.length; j < jl; j ++ ) {\n\n var camera2 = cameras[ j ];\n\n if ( object.layers.test( camera2.layers ) ) {\n\n var bounds = camera2.bounds;\n\n var x = bounds.x * _width;\n var y = bounds.y * _height;\n var width = bounds.z * _width;\n var height = bounds.w * _height;\n\n state.viewport( _currentViewport.set( x, y, width, height ).multiplyScalar( _pixelRatio ) );\n\n renderObject( object, scene, camera2, geometry, material, group );\n\n }\n\n }\n\n } else {\n\n _currentArrayCamera = null;\n\n renderObject( object, scene, camera, geometry, material, group );\n\n }\n\n }\n\n }\n\n function renderObject( object, scene, camera, geometry, material, group ) {\n\n object.onBeforeRender( _this, scene, camera, geometry, material, group );\n currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );\n\n object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );\n object.normalMatrix.getNormalMatrix( object.modelViewMatrix );\n\n if ( object.isImmediateRenderObject ) {\n\n var frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );\n\n state.setMaterial( material, frontFaceCW );\n\n var program = setProgram( camera, scene.fog, material, object );\n\n _currentGeometryProgram = '';\n\n renderObjectImmediate( object, program, material );\n\n } else {\n\n _this.renderBufferDirect( camera, scene.fog, geometry, material, object, group );\n\n }\n\n object.onAfterRender( _this, scene, camera, geometry, material, group );\n currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );\n\n }\n\n function initMaterial( material, fog, object ) {\n\n var materialProperties = properties.get( material );\n\n var lights = currentRenderState.state.lights;\n var shadowsArray = currentRenderState.state.shadowsArray;\n\n var parameters = programCache.getParameters(\n material, lights.state, shadowsArray, fog, _clipping.numPlanes, _clipping.numIntersection, object );\n\n var code = programCache.getProgramCode( material, parameters );\n\n var program = materialProperties.program;\n var programChange = true;\n\n if ( program === undefined ) {\n\n // new material\n material.addEventListener( 'dispose', onMaterialDispose );\n\n } else if ( program.code !== code ) {\n\n // changed glsl or parameters\n releaseMaterialProgramReference( material );\n\n } else if ( materialProperties.lightsHash !== lights.state.hash ) {\n\n properties.update( material, 'lightsHash', lights.state.hash );\n programChange = false;\n\n } else if ( parameters.shaderID !== undefined ) {\n\n // same glsl and uniform list\n return;\n\n } else {\n\n // only rebuild uniform list\n programChange = false;\n\n }\n\n if ( programChange ) {\n\n if ( parameters.shaderID ) {\n\n var shader = ShaderLib[ parameters.shaderID ];\n\n materialProperties.shader = {\n name: material.type,\n uniforms: UniformsUtils.clone( shader.uniforms ),\n vertexShader: shader.vertexShader,\n fragmentShader: shader.fragmentShader\n };\n\n } else {\n\n materialProperties.shader = {\n name: material.type,\n uniforms: material.uniforms,\n vertexShader: material.vertexShader,\n fragmentShader: material.fragmentShader\n };\n\n }\n\n material.onBeforeCompile( materialProperties.shader, _this );\n\n program = programCache.acquireProgram( material, materialProperties.shader, parameters, code );\n\n materialProperties.program = program;\n material.program = program;\n\n }\n\n var programAttributes = program.getAttributes();\n\n if ( material.morphTargets ) {\n\n material.numSupportedMorphTargets = 0;\n\n for ( var i = 0; i < _this.maxMorphTargets; i ++ ) {\n\n if ( programAttributes[ 'morphTarget' + i ] >= 0 ) {\n\n material.numSupportedMorphTargets ++;\n\n }\n\n }\n\n }\n\n if ( material.morphNormals ) {\n\n material.numSupportedMorphNormals = 0;\n\n for ( var i = 0; i < _this.maxMorphNormals; i ++ ) {\n\n if ( programAttributes[ 'morphNormal' + i ] >= 0 ) {\n\n material.numSupportedMorphNormals ++;\n\n }\n\n }\n\n }\n\n var uniforms = materialProperties.shader.uniforms;\n\n if ( ! material.isShaderMaterial &&\n ! material.isRawShaderMaterial ||\n material.clipping === true ) {\n\n materialProperties.numClippingPlanes = _clipping.numPlanes;\n materialProperties.numIntersection = _clipping.numIntersection;\n uniforms.clippingPlanes = _clipping.uniform;\n\n }\n\n materialProperties.fog = fog;\n\n // store the light setup it was created for\n\n materialProperties.lightsHash = lights.state.hash;\n\n if ( material.lights ) {\n\n // wire up the material to this renderer's lighting state\n\n uniforms.ambientLightColor.value = lights.state.ambient;\n uniforms.directionalLights.value = lights.state.directional;\n uniforms.spotLights.value = lights.state.spot;\n uniforms.rectAreaLights.value = lights.state.rectArea;\n uniforms.pointLights.value = lights.state.point;\n uniforms.hemisphereLights.value = lights.state.hemi;\n\n uniforms.directionalShadowMap.value = lights.state.directionalShadowMap;\n uniforms.directionalShadowMatrix.value = lights.state.directionalShadowMatrix;\n uniforms.spotShadowMap.value = lights.state.spotShadowMap;\n uniforms.spotShadowMatrix.value = lights.state.spotShadowMatrix;\n uniforms.pointShadowMap.value = lights.state.pointShadowMap;\n uniforms.pointShadowMatrix.value = lights.state.pointShadowMatrix;\n // TODO (abelnation): add area lights shadow info to uniforms\n\n }\n\n var progUniforms = materialProperties.program.getUniforms(),\n uniformsList =\n WebGLUniforms.seqWithValue( progUniforms.seq, uniforms );\n\n materialProperties.uniformsList = uniformsList;\n\n }\n\n function setProgram( camera, fog, material, object ) {\n\n _usedTextureUnits = 0;\n\n var materialProperties = properties.get( material );\n var lights = currentRenderState.state.lights;\n\n if ( _clippingEnabled ) {\n\n if ( _localClippingEnabled || camera !== _currentCamera ) {\n\n var useCache =\n camera === _currentCamera &&\n material.id === _currentMaterialId;\n\n // we might want to call this function with some ClippingGroup\n // object instead of the material, once it becomes feasible\n // (#8465, #8379)\n _clipping.setState(\n material.clippingPlanes, material.clipIntersection, material.clipShadows,\n camera, materialProperties, useCache );\n\n }\n\n }\n\n if ( material.needsUpdate === false ) {\n\n if ( materialProperties.program === undefined ) {\n\n material.needsUpdate = true;\n\n } else if ( material.fog && materialProperties.fog !== fog ) {\n\n material.needsUpdate = true;\n\n } else if ( material.lights && materialProperties.lightsHash !== lights.state.hash ) {\n\n material.needsUpdate = true;\n\n } else if ( materialProperties.numClippingPlanes !== undefined &&\n ( materialProperties.numClippingPlanes !== _clipping.numPlanes ||\n materialProperties.numIntersection !== _clipping.numIntersection ) ) {\n\n material.needsUpdate = true;\n\n }\n\n }\n\n if ( material.needsUpdate ) {\n\n initMaterial( material, fog, object );\n material.needsUpdate = false;\n\n }\n\n var refreshProgram = false;\n var refreshMaterial = false;\n var refreshLights = false;\n\n var program = materialProperties.program,\n p_uniforms = program.getUniforms(),\n m_uniforms = materialProperties.shader.uniforms;\n\n if ( state.useProgram( program.program ) ) {\n\n refreshProgram = true;\n refreshMaterial = true;\n refreshLights = true;\n\n }\n\n if ( material.id !== _currentMaterialId ) {\n\n _currentMaterialId = material.id;\n\n refreshMaterial = true;\n\n }\n\n if ( refreshProgram || camera !== _currentCamera ) {\n\n p_uniforms.setValue( _gl, 'projectionMatrix', camera.projectionMatrix );\n\n if ( capabilities.logarithmicDepthBuffer ) {\n\n p_uniforms.setValue( _gl, 'logDepthBufFC',\n 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );\n\n }\n\n // Avoid unneeded uniform updates per ArrayCamera's sub-camera\n\n if ( _currentCamera !== ( _currentArrayCamera || camera ) ) {\n\n _currentCamera = ( _currentArrayCamera || camera );\n\n // lighting uniforms depend on the camera so enforce an update\n // now, in case this material supports lights - or later, when\n // the next material that does gets activated:\n\n refreshMaterial = true; // set to true on material change\n refreshLights = true; // remains set until update done\n\n }\n\n // load material specific uniforms\n // (shader material also gets them for the sake of genericity)\n\n if ( material.isShaderMaterial ||\n material.isMeshPhongMaterial ||\n material.isMeshStandardMaterial ||\n material.envMap ) {\n\n var uCamPos = p_uniforms.map.cameraPosition;\n\n if ( uCamPos !== undefined ) {\n\n uCamPos.setValue( _gl,\n _vector3.setFromMatrixPosition( camera.matrixWorld ) );\n\n }\n\n }\n\n if ( material.isMeshPhongMaterial ||\n material.isMeshLambertMaterial ||\n material.isMeshBasicMaterial ||\n material.isMeshStandardMaterial ||\n material.isShaderMaterial ||\n material.skinning ) {\n\n p_uniforms.setValue( _gl, 'viewMatrix', camera.matrixWorldInverse );\n\n }\n\n }\n\n // skinning uniforms must be set even if material didn't change\n // auto-setting of texture unit for bone texture must go before other textures\n // not sure why, but otherwise weird things happen\n\n if ( material.skinning ) {\n\n p_uniforms.setOptional( _gl, object, 'bindMatrix' );\n p_uniforms.setOptional( _gl, object, 'bindMatrixInverse' );\n\n var skeleton = object.skeleton;\n\n if ( skeleton ) {\n\n var bones = skeleton.bones;\n\n if ( capabilities.floatVertexTextures ) {\n\n if ( skeleton.boneTexture === undefined ) {\n\n // layout (1 matrix = 4 pixels)\n // RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4)\n // with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8)\n // 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16)\n // 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32)\n // 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64)\n\n\n var size = Math.sqrt( bones.length * 4 ); // 4 pixels needed for 1 matrix\n size = _Math.ceilPowerOfTwo( size );\n size = Math.max( size, 4 );\n\n var boneMatrices = new Float32Array( size * size * 4 ); // 4 floats per RGBA pixel\n boneMatrices.set( skeleton.boneMatrices ); // copy current values\n\n var boneTexture = new DataTexture( boneMatrices, size, size, RGBAFormat, FloatType );\n boneTexture.needsUpdate = true;\n\n skeleton.boneMatrices = boneMatrices;\n skeleton.boneTexture = boneTexture;\n skeleton.boneTextureSize = size;\n\n }\n\n p_uniforms.setValue( _gl, 'boneTexture', skeleton.boneTexture );\n p_uniforms.setValue( _gl, 'boneTextureSize', skeleton.boneTextureSize );\n\n } else {\n\n p_uniforms.setOptional( _gl, skeleton, 'boneMatrices' );\n\n }\n\n }\n\n }\n\n if ( refreshMaterial ) {\n\n p_uniforms.setValue( _gl, 'toneMappingExposure', _this.toneMappingExposure );\n p_uniforms.setValue( _gl, 'toneMappingWhitePoint', _this.toneMappingWhitePoint );\n\n if ( material.lights ) {\n\n // the current material requires lighting info\n\n // note: all lighting uniforms are always set correctly\n // they simply reference the renderer's state for their\n // values\n //\n // use the current material's .needsUpdate flags to set\n // the GL state when required\n\n markUniformsLightsNeedsUpdate( m_uniforms, refreshLights );\n\n }\n\n // refresh uniforms common to several materials\n\n if ( fog && material.fog ) {\n\n refreshUniformsFog( m_uniforms, fog );\n\n }\n\n if ( material.isMeshBasicMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n } else if ( material.isMeshLambertMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsLambert( m_uniforms, material );\n\n } else if ( material.isMeshPhongMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n if ( material.isMeshToonMaterial ) {\n\n refreshUniformsToon( m_uniforms, material );\n\n } else {\n\n refreshUniformsPhong( m_uniforms, material );\n\n }\n\n } else if ( material.isMeshStandardMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n\n if ( material.isMeshPhysicalMaterial ) {\n\n refreshUniformsPhysical( m_uniforms, material );\n\n } else {\n\n refreshUniformsStandard( m_uniforms, material );\n\n }\n\n } else if ( material.isMeshDepthMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsDepth( m_uniforms, material );\n\n } else if ( material.isMeshDistanceMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsDistance( m_uniforms, material );\n\n } else if ( material.isMeshNormalMaterial ) {\n\n refreshUniformsCommon( m_uniforms, material );\n refreshUniformsNormal( m_uniforms, material );\n\n } else if ( material.isLineBasicMaterial ) {\n\n refreshUniformsLine( m_uniforms, material );\n\n if ( material.isLineDashedMaterial ) {\n\n refreshUniformsDash( m_uniforms, material );\n\n }\n\n } else if ( material.isPointsMaterial ) {\n\n refreshUniformsPoints( m_uniforms, material );\n\n } else if ( material.isShadowMaterial ) {\n\n m_uniforms.color.value = material.color;\n m_uniforms.opacity.value = material.opacity;\n\n }\n\n // RectAreaLight Texture\n // TODO (mrdoob): Find a nicer implementation\n\n if ( m_uniforms.ltc_1 !== undefined ) m_uniforms.ltc_1.value = UniformsLib.LTC_1;\n if ( m_uniforms.ltc_2 !== undefined ) m_uniforms.ltc_2.value = UniformsLib.LTC_2;\n\n WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );\n\n }\n\n if ( material.isShaderMaterial && material.uniformsNeedUpdate === true ) {\n\n WebGLUniforms.upload( _gl, materialProperties.uniformsList, m_uniforms, _this );\n material.uniformsNeedUpdate = false;\n\n }\n\n // common matrices\n\n p_uniforms.setValue( _gl, 'modelViewMatrix', object.modelViewMatrix );\n p_uniforms.setValue( _gl, 'normalMatrix', object.normalMatrix );\n p_uniforms.setValue( _gl, 'modelMatrix', object.matrixWorld );\n\n return program;\n\n }\n\n // Uniforms (refresh uniforms objects)\n\n function refreshUniformsCommon( uniforms, material ) {\n\n uniforms.opacity.value = material.opacity;\n\n if ( material.color ) {\n\n uniforms.diffuse.value = material.color;\n\n }\n\n if ( material.emissive ) {\n\n uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );\n\n }\n\n if ( material.map ) {\n\n uniforms.map.value = material.map;\n\n }\n\n if ( material.alphaMap ) {\n\n uniforms.alphaMap.value = material.alphaMap;\n\n }\n\n if ( material.specularMap ) {\n\n uniforms.specularMap.value = material.specularMap;\n\n }\n\n if ( material.envMap ) {\n\n uniforms.envMap.value = material.envMap;\n\n // don't flip CubeTexture envMaps, flip everything else:\n // WebGLRenderTargetCube will be flipped for backwards compatibility\n // WebGLRenderTargetCube.texture will be flipped because it's a Texture and NOT a CubeTexture\n // this check must be handled differently, or removed entirely, if WebGLRenderTargetCube uses a CubeTexture in the future\n uniforms.flipEnvMap.value = ( ! ( material.envMap && material.envMap.isCubeTexture ) ) ? 1 : - 1;\n\n uniforms.reflectivity.value = material.reflectivity;\n uniforms.refractionRatio.value = material.refractionRatio;\n\n uniforms.maxMipLevel.value = properties.get( material.envMap ).__maxMipLevel;\n\n }\n\n if ( material.lightMap ) {\n\n uniforms.lightMap.value = material.lightMap;\n uniforms.lightMapIntensity.value = material.lightMapIntensity;\n\n }\n\n if ( material.aoMap ) {\n\n uniforms.aoMap.value = material.aoMap;\n uniforms.aoMapIntensity.value = material.aoMapIntensity;\n\n }\n\n // uv repeat and offset setting priorities\n // 1. color map\n // 2. specular map\n // 3. normal map\n // 4. bump map\n // 5. alpha map\n // 6. emissive map\n\n var uvScaleMap;\n\n if ( material.map ) {\n\n uvScaleMap = material.map;\n\n } else if ( material.specularMap ) {\n\n uvScaleMap = material.specularMap;\n\n } else if ( material.displacementMap ) {\n\n uvScaleMap = material.displacementMap;\n\n } else if ( material.normalMap ) {\n\n uvScaleMap = material.normalMap;\n\n } else if ( material.bumpMap ) {\n\n uvScaleMap = material.bumpMap;\n\n } else if ( material.roughnessMap ) {\n\n uvScaleMap = material.roughnessMap;\n\n } else if ( material.metalnessMap ) {\n\n uvScaleMap = material.metalnessMap;\n\n } else if ( material.alphaMap ) {\n\n uvScaleMap = material.alphaMap;\n\n } else if ( material.emissiveMap ) {\n\n uvScaleMap = material.emissiveMap;\n\n }\n\n if ( uvScaleMap !== undefined ) {\n\n // backwards compatibility\n if ( uvScaleMap.isWebGLRenderTarget ) {\n\n uvScaleMap = uvScaleMap.texture;\n\n }\n\n if ( uvScaleMap.matrixAutoUpdate === true ) {\n\n var offset = uvScaleMap.offset;\n var repeat = uvScaleMap.repeat;\n var rotation = uvScaleMap.rotation;\n var center = uvScaleMap.center;\n\n uvScaleMap.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y );\n\n }\n\n uniforms.uvTransform.value.copy( uvScaleMap.matrix );\n\n }\n\n }\n\n function refreshUniformsLine( uniforms, material ) {\n\n uniforms.diffuse.value = material.color;\n uniforms.opacity.value = material.opacity;\n\n }\n\n function refreshUniformsDash( uniforms, material ) {\n\n uniforms.dashSize.value = material.dashSize;\n uniforms.totalSize.value = material.dashSize + material.gapSize;\n uniforms.scale.value = material.scale;\n\n }\n\n function refreshUniformsPoints( uniforms, material ) {\n\n uniforms.diffuse.value = material.color;\n uniforms.opacity.value = material.opacity;\n uniforms.size.value = material.size * _pixelRatio;\n uniforms.scale.value = _height * 0.5;\n\n uniforms.map.value = material.map;\n\n if ( material.map !== null ) {\n\n if ( material.map.matrixAutoUpdate === true ) {\n\n var offset = material.map.offset;\n var repeat = material.map.repeat;\n var rotation = material.map.rotation;\n var center = material.map.center;\n\n material.map.matrix.setUvTransform( offset.x, offset.y, repeat.x, repeat.y, rotation, center.x, center.y );\n\n }\n\n uniforms.uvTransform.value.copy( material.map.matrix );\n\n }\n\n }\n\n function refreshUniformsFog( uniforms, fog ) {\n\n uniforms.fogColor.value = fog.color;\n\n if ( fog.isFog ) {\n\n uniforms.fogNear.value = fog.near;\n uniforms.fogFar.value = fog.far;\n\n } else if ( fog.isFogExp2 ) {\n\n uniforms.fogDensity.value = fog.density;\n\n }\n\n }\n\n function refreshUniformsLambert( uniforms, material ) {\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n }\n\n function refreshUniformsPhong( uniforms, material ) {\n\n uniforms.specular.value = material.specular;\n uniforms.shininess.value = Math.max( material.shininess, 1e-4 ); // to prevent pow( 0.0, 0.0 )\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n function refreshUniformsToon( uniforms, material ) {\n\n refreshUniformsPhong( uniforms, material );\n\n if ( material.gradientMap ) {\n\n uniforms.gradientMap.value = material.gradientMap;\n\n }\n\n }\n\n function refreshUniformsStandard( uniforms, material ) {\n\n uniforms.roughness.value = material.roughness;\n uniforms.metalness.value = material.metalness;\n\n if ( material.roughnessMap ) {\n\n uniforms.roughnessMap.value = material.roughnessMap;\n\n }\n\n if ( material.metalnessMap ) {\n\n uniforms.metalnessMap.value = material.metalnessMap;\n\n }\n\n if ( material.emissiveMap ) {\n\n uniforms.emissiveMap.value = material.emissiveMap;\n\n }\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n if ( material.envMap ) {\n\n //uniforms.envMap.value = material.envMap; // part of uniforms common\n uniforms.envMapIntensity.value = material.envMapIntensity;\n\n }\n\n }\n\n function refreshUniformsPhysical( uniforms, material ) {\n\n uniforms.clearCoat.value = material.clearCoat;\n uniforms.clearCoatRoughness.value = material.clearCoatRoughness;\n\n refreshUniformsStandard( uniforms, material );\n\n }\n\n function refreshUniformsDepth( uniforms, material ) {\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n function refreshUniformsDistance( uniforms, material ) {\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n uniforms.referencePosition.value.copy( material.referencePosition );\n uniforms.nearDistance.value = material.nearDistance;\n uniforms.farDistance.value = material.farDistance;\n\n }\n\n function refreshUniformsNormal( uniforms, material ) {\n\n if ( material.bumpMap ) {\n\n uniforms.bumpMap.value = material.bumpMap;\n uniforms.bumpScale.value = material.bumpScale;\n\n }\n\n if ( material.normalMap ) {\n\n uniforms.normalMap.value = material.normalMap;\n uniforms.normalScale.value.copy( material.normalScale );\n\n }\n\n if ( material.displacementMap ) {\n\n uniforms.displacementMap.value = material.displacementMap;\n uniforms.displacementScale.value = material.displacementScale;\n uniforms.displacementBias.value = material.displacementBias;\n\n }\n\n }\n\n // If uniforms are marked as clean, they don't need to be loaded to the GPU.\n\n function markUniformsLightsNeedsUpdate( uniforms, value ) {\n\n uniforms.ambientLightColor.needsUpdate = value;\n\n uniforms.directionalLights.needsUpdate = value;\n uniforms.pointLights.needsUpdate = value;\n uniforms.spotLights.needsUpdate = value;\n uniforms.rectAreaLights.needsUpdate = value;\n uniforms.hemisphereLights.needsUpdate = value;\n\n }\n\n // Textures\n\n function allocTextureUnit() {\n\n var textureUnit = _usedTextureUnits;\n\n if ( textureUnit >= capabilities.maxTextures ) {\n\n console.warn( 'THREE.WebGLRenderer: Trying to use ' + textureUnit + ' texture units while this GPU supports only ' + capabilities.maxTextures );\n\n }\n\n _usedTextureUnits += 1;\n\n return textureUnit;\n\n }\n\n this.allocTextureUnit = allocTextureUnit;\n\n // this.setTexture2D = setTexture2D;\n this.setTexture2D = ( function () {\n\n var warned = false;\n\n // backwards compatibility: peel texture.texture\n return function setTexture2D( texture, slot ) {\n\n if ( texture && texture.isWebGLRenderTarget ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer.setTexture2D: don't use render targets as textures. Use their .texture property instead.\" );\n warned = true;\n\n }\n\n texture = texture.texture;\n\n }\n\n textures.setTexture2D( texture, slot );\n\n };\n\n }() );\n\n this.setTexture = ( function () {\n\n var warned = false;\n\n return function setTexture( texture, slot ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer: .setTexture is deprecated, use setTexture2D instead.\" );\n warned = true;\n\n }\n\n textures.setTexture2D( texture, slot );\n\n };\n\n }() );\n\n this.setTextureCube = ( function () {\n\n var warned = false;\n\n return function setTextureCube( texture, slot ) {\n\n // backwards compatibility: peel texture.texture\n if ( texture && texture.isWebGLRenderTargetCube ) {\n\n if ( ! warned ) {\n\n console.warn( \"THREE.WebGLRenderer.setTextureCube: don't use cube render targets as textures. Use their .texture property instead.\" );\n warned = true;\n\n }\n\n texture = texture.texture;\n\n }\n\n // currently relying on the fact that WebGLRenderTargetCube.texture is a Texture and NOT a CubeTexture\n // TODO: unify these code paths\n if ( ( texture && texture.isCubeTexture ) ||\n ( Array.isArray( texture.image ) && texture.image.length === 6 ) ) {\n\n // CompressedTexture can have Array in image :/\n\n // this function alone should take care of cube textures\n textures.setTextureCube( texture, slot );\n\n } else {\n\n // assumed: texture property of THREE.WebGLRenderTargetCube\n\n textures.setTextureCubeDynamic( texture, slot );\n\n }\n\n };\n\n }() );\n\n this.getRenderTarget = function () {\n\n return _currentRenderTarget;\n\n };\n\n this.setRenderTarget = function ( renderTarget ) {\n\n _currentRenderTarget = renderTarget;\n\n if ( renderTarget && properties.get( renderTarget ).__webglFramebuffer === undefined ) {\n\n textures.setupRenderTarget( renderTarget );\n\n }\n\n var framebuffer = null;\n var isCube = false;\n\n if ( renderTarget ) {\n\n var __webglFramebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n if ( renderTarget.isWebGLRenderTargetCube ) {\n\n framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ];\n isCube = true;\n\n } else {\n\n framebuffer = __webglFramebuffer;\n\n }\n\n _currentViewport.copy( renderTarget.viewport );\n _currentScissor.copy( renderTarget.scissor );\n _currentScissorTest = renderTarget.scissorTest;\n\n } else {\n\n _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio );\n _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio );\n _currentScissorTest = _scissorTest;\n\n }\n\n if ( _currentFramebuffer !== framebuffer ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n _currentFramebuffer = framebuffer;\n\n }\n\n state.viewport( _currentViewport );\n state.scissor( _currentScissor );\n state.setScissorTest( _currentScissorTest );\n\n if ( isCube ) {\n\n var textureProperties = properties.get( renderTarget.texture );\n _gl.framebufferTexture2D( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + renderTarget.activeCubeFace, textureProperties.__webglTexture, renderTarget.activeMipMapLevel );\n\n }\n\n };\n\n this.readRenderTargetPixels = function ( renderTarget, x, y, width, height, buffer ) {\n\n if ( ! ( renderTarget && renderTarget.isWebGLRenderTarget ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.' );\n return;\n\n }\n\n var framebuffer = properties.get( renderTarget ).__webglFramebuffer;\n\n if ( framebuffer ) {\n\n var restore = false;\n\n if ( framebuffer !== _currentFramebuffer ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );\n\n restore = true;\n\n }\n\n try {\n\n var texture = renderTarget.texture;\n var textureFormat = texture.format;\n var textureType = texture.type;\n\n if ( textureFormat !== RGBAFormat && utils.convert( textureFormat ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_FORMAT ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in RGBA or implementation defined format.' );\n return;\n\n }\n\n if ( textureType !== UnsignedByteType && utils.convert( textureType ) !== _gl.getParameter( _gl.IMPLEMENTATION_COLOR_READ_TYPE ) && // IE11, Edge and Chrome Mac < 52 (#9513)\n ! ( textureType === FloatType && ( extensions.get( 'OES_texture_float' ) || extensions.get( 'WEBGL_color_buffer_float' ) ) ) && // Chrome Mac >= 52 and Firefox\n ! ( textureType === HalfFloatType && extensions.get( 'EXT_color_buffer_half_float' ) ) ) {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not in UnsignedByteType or implementation defined type.' );\n return;\n\n }\n\n if ( _gl.checkFramebufferStatus( _gl.FRAMEBUFFER ) === _gl.FRAMEBUFFER_COMPLETE ) {\n\n // the following if statement ensures valid read requests (no out-of-bounds pixels, see #8604)\n\n if ( ( x >= 0 && x <= ( renderTarget.width - width ) ) && ( y >= 0 && y <= ( renderTarget.height - height ) ) ) {\n\n _gl.readPixels( x, y, width, height, utils.convert( textureFormat ), utils.convert( textureType ), buffer );\n\n }\n\n } else {\n\n console.error( 'THREE.WebGLRenderer.readRenderTargetPixels: readPixels from renderTarget failed. Framebuffer not complete.' );\n\n }\n\n } finally {\n\n if ( restore ) {\n\n _gl.bindFramebuffer( _gl.FRAMEBUFFER, _currentFramebuffer );\n\n }\n\n }\n\n }\n\n };\n\n this.copyFramebufferToTexture = function ( position, texture, level ) {\n\n var width = texture.image.width;\n var height = texture.image.height;\n var glFormat = utils.convert( texture.format );\n\n this.setTexture2D( texture, 0 );\n\n _gl.copyTexImage2D( _gl.TEXTURE_2D, level || 0, glFormat, position.x, position.y, width, height, 0 );\n\n };\n\n this.copyTextureToTexture = function ( position, srcTexture, dstTexture, level ) {\n\n var width = srcTexture.image.width;\n var height = srcTexture.image.height;\n var glFormat = utils.convert( dstTexture.format );\n var glType = utils.convert( dstTexture.type );\n var pixels = srcTexture.isDataTexture ? srcTexture.image.data : srcTexture.image;\n\n this.setTexture2D( dstTexture, 0 );\n\n _gl.texSubImage2D( _gl.TEXTURE_2D, level || 0, position.x, position.y, width, height, glFormat, glType, pixels );\n\n };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function FogExp2( color, density ) {\n\n this.name = '';\n\n this.color = new Color( color );\n this.density = ( density !== undefined ) ? density : 0.00025;\n\n }\n\n FogExp2.prototype.isFogExp2 = true;\n\n FogExp2.prototype.clone = function () {\n\n return new FogExp2( this.color.getHex(), this.density );\n\n };\n\n FogExp2.prototype.toJSON = function ( /* meta */ ) {\n\n return {\n type: 'FogExp2',\n color: this.color.getHex(),\n density: this.density\n };\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Fog( color, near, far ) {\n\n this.name = '';\n\n this.color = new Color( color );\n\n this.near = ( near !== undefined ) ? near : 1;\n this.far = ( far !== undefined ) ? far : 1000;\n\n }\n\n Fog.prototype.isFog = true;\n\n Fog.prototype.clone = function () {\n\n return new Fog( this.color.getHex(), this.near, this.far );\n\n };\n\n Fog.prototype.toJSON = function ( /* meta */ ) {\n\n return {\n type: 'Fog',\n color: this.color.getHex(),\n near: this.near,\n far: this.far\n };\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Scene() {\n\n Object3D.call( this );\n\n this.type = 'Scene';\n\n this.background = null;\n this.fog = null;\n this.overrideMaterial = null;\n\n this.autoUpdate = true; // checked by the renderer\n\n }\n\n Scene.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Scene,\n\n copy: function ( source, recursive ) {\n\n Object3D.prototype.copy.call( this, source, recursive );\n\n if ( source.background !== null ) this.background = source.background.clone();\n if ( source.fog !== null ) this.fog = source.fog.clone();\n if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone();\n\n this.autoUpdate = source.autoUpdate;\n this.matrixAutoUpdate = source.matrixAutoUpdate;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n if ( this.background !== null ) data.object.background = this.background.toJSON( meta );\n if ( this.fog !== null ) data.object.fog = this.fog.toJSON();\n\n return data;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * uvOffset: new THREE.Vector2(),\n * uvScale: new THREE.Vector2()\n * }\n */\n\n function SpriteMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'SpriteMaterial';\n\n this.color = new Color( 0xffffff );\n this.map = null;\n\n this.rotation = 0;\n\n this.fog = false;\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n SpriteMaterial.prototype = Object.create( Material.prototype );\n SpriteMaterial.prototype.constructor = SpriteMaterial;\n SpriteMaterial.prototype.isSpriteMaterial = true;\n\n SpriteMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.map = source.map;\n\n this.rotation = source.rotation;\n\n return this;\n\n };\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Sprite( material ) {\n\n Object3D.call( this );\n\n this.type = 'Sprite';\n\n this.material = ( material !== undefined ) ? material : new SpriteMaterial();\n\n this.center = new Vector2( 0.5, 0.5 );\n\n }\n\n Sprite.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Sprite,\n\n isSprite: true,\n\n raycast: ( function () {\n\n var intersectPoint = new Vector3();\n var worldPosition = new Vector3();\n var worldScale = new Vector3();\n\n return function raycast( raycaster, intersects ) {\n\n worldPosition.setFromMatrixPosition( this.matrixWorld );\n raycaster.ray.closestPointToPoint( worldPosition, intersectPoint );\n\n worldScale.setFromMatrixScale( this.matrixWorld );\n var guessSizeSq = worldScale.x * worldScale.y / 4;\n\n if ( worldPosition.distanceToSquared( intersectPoint ) > guessSizeSq ) return;\n\n var distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return;\n\n intersects.push( {\n\n distance: distance,\n point: intersectPoint.clone(),\n face: null,\n object: this\n\n } );\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.material ).copy( this );\n\n },\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n if ( source.center !== undefined ) this.center.copy( source.center );\n\n return this;\n\n }\n\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LOD() {\n\n Object3D.call( this );\n\n this.type = 'LOD';\n\n Object.defineProperties( this, {\n levels: {\n enumerable: true,\n value: []\n }\n } );\n\n }\n\n LOD.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: LOD,\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source, false );\n\n var levels = source.levels;\n\n for ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n var level = levels[ i ];\n\n this.addLevel( level.object.clone(), level.distance );\n\n }\n\n return this;\n\n },\n\n addLevel: function ( object, distance ) {\n\n if ( distance === undefined ) distance = 0;\n\n distance = Math.abs( distance );\n\n var levels = this.levels;\n\n for ( var l = 0; l < levels.length; l ++ ) {\n\n if ( distance < levels[ l ].distance ) {\n\n break;\n\n }\n\n }\n\n levels.splice( l, 0, { distance: distance, object: object } );\n\n this.add( object );\n\n },\n\n getObjectForDistance: function ( distance ) {\n\n var levels = this.levels;\n\n for ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n if ( distance < levels[ i ].distance ) {\n\n break;\n\n }\n\n }\n\n return levels[ i - 1 ].object;\n\n },\n\n raycast: ( function () {\n\n var matrixPosition = new Vector3();\n\n return function raycast( raycaster, intersects ) {\n\n matrixPosition.setFromMatrixPosition( this.matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( matrixPosition );\n\n this.getObjectForDistance( distance ).raycast( raycaster, intersects );\n\n };\n\n }() ),\n\n update: function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n\n return function update( camera ) {\n\n var levels = this.levels;\n\n if ( levels.length > 1 ) {\n\n v1.setFromMatrixPosition( camera.matrixWorld );\n v2.setFromMatrixPosition( this.matrixWorld );\n\n var distance = v1.distanceTo( v2 );\n\n levels[ 0 ].object.visible = true;\n\n for ( var i = 1, l = levels.length; i < l; i ++ ) {\n\n if ( distance >= levels[ i ].distance ) {\n\n levels[ i - 1 ].object.visible = false;\n levels[ i ].object.visible = true;\n\n } else {\n\n break;\n\n }\n\n }\n\n for ( ; i < l; i ++ ) {\n\n levels[ i ].object.visible = false;\n\n }\n\n }\n\n };\n\n }(),\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.levels = [];\n\n var levels = this.levels;\n\n for ( var i = 0, l = levels.length; i < l; i ++ ) {\n\n var level = levels[ i ];\n\n data.object.levels.push( {\n object: level.object.uuid,\n distance: level.distance\n } );\n\n }\n\n return data;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author michael guerrero / http://realitymeltdown.com\n * @author ikerr / http://verold.com\n */\n\n function Skeleton( bones, boneInverses ) {\n\n // copy the bone array\n\n bones = bones || [];\n\n this.bones = bones.slice( 0 );\n this.boneMatrices = new Float32Array( this.bones.length * 16 );\n\n // use the supplied bone inverses or calculate the inverses\n\n if ( boneInverses === undefined ) {\n\n this.calculateInverses();\n\n } else {\n\n if ( this.bones.length === boneInverses.length ) {\n\n this.boneInverses = boneInverses.slice( 0 );\n\n } else {\n\n console.warn( 'THREE.Skeleton boneInverses is the wrong length.' );\n\n this.boneInverses = [];\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n this.boneInverses.push( new Matrix4() );\n\n }\n\n }\n\n }\n\n }\n\n Object.assign( Skeleton.prototype, {\n\n calculateInverses: function () {\n\n this.boneInverses = [];\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n var inverse = new Matrix4();\n\n if ( this.bones[ i ] ) {\n\n inverse.getInverse( this.bones[ i ].matrixWorld );\n\n }\n\n this.boneInverses.push( inverse );\n\n }\n\n },\n\n pose: function () {\n\n var bone, i, il;\n\n // recover the bind-time world matrices\n\n for ( i = 0, il = this.bones.length; i < il; i ++ ) {\n\n bone = this.bones[ i ];\n\n if ( bone ) {\n\n bone.matrixWorld.getInverse( this.boneInverses[ i ] );\n\n }\n\n }\n\n // compute the local matrices, positions, rotations and scales\n\n for ( i = 0, il = this.bones.length; i < il; i ++ ) {\n\n bone = this.bones[ i ];\n\n if ( bone ) {\n\n if ( bone.parent && bone.parent.isBone ) {\n\n bone.matrix.getInverse( bone.parent.matrixWorld );\n bone.matrix.multiply( bone.matrixWorld );\n\n } else {\n\n bone.matrix.copy( bone.matrixWorld );\n\n }\n\n bone.matrix.decompose( bone.position, bone.quaternion, bone.scale );\n\n }\n\n }\n\n },\n\n update: ( function () {\n\n var offsetMatrix = new Matrix4();\n var identityMatrix = new Matrix4();\n\n return function update() {\n\n var bones = this.bones;\n var boneInverses = this.boneInverses;\n var boneMatrices = this.boneMatrices;\n var boneTexture = this.boneTexture;\n\n // flatten bone matrices to array\n\n for ( var i = 0, il = bones.length; i < il; i ++ ) {\n\n // compute the offset between the current and the original transform\n\n var matrix = bones[ i ] ? bones[ i ].matrixWorld : identityMatrix;\n\n offsetMatrix.multiplyMatrices( matrix, boneInverses[ i ] );\n offsetMatrix.toArray( boneMatrices, i * 16 );\n\n }\n\n if ( boneTexture !== undefined ) {\n\n boneTexture.needsUpdate = true;\n\n }\n\n };\n\n } )(),\n\n clone: function () {\n\n return new Skeleton( this.bones, this.boneInverses );\n\n },\n\n getBoneByName: function ( name ) {\n\n for ( var i = 0, il = this.bones.length; i < il; i ++ ) {\n\n var bone = this.bones[ i ];\n\n if ( bone.name === name ) {\n\n return bone;\n\n }\n\n }\n\n return undefined;\n\n }\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author ikerr / http://verold.com\n */\n\n function Bone() {\n\n Object3D.call( this );\n\n this.type = 'Bone';\n\n }\n\n Bone.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Bone,\n\n isBone: true\n\n } );\n\n /**\n * @author mikael emtinger / http://gomo.se/\n * @author alteredq / http://alteredqualia.com/\n * @author ikerr / http://verold.com\n */\n\n function SkinnedMesh( geometry, material ) {\n\n Mesh.call( this, geometry, material );\n\n this.type = 'SkinnedMesh';\n\n this.bindMode = 'attached';\n this.bindMatrix = new Matrix4();\n this.bindMatrixInverse = new Matrix4();\n\n var bones = this.initBones();\n var skeleton = new Skeleton( bones );\n\n this.bind( skeleton, this.matrixWorld );\n\n this.normalizeSkinWeights();\n\n }\n\n SkinnedMesh.prototype = Object.assign( Object.create( Mesh.prototype ), {\n\n constructor: SkinnedMesh,\n\n isSkinnedMesh: true,\n\n initBones: function () {\n\n var bones = [], bone, gbone;\n var i, il;\n\n if ( this.geometry && this.geometry.bones !== undefined ) {\n\n // first, create array of 'Bone' objects from geometry data\n\n for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {\n\n gbone = this.geometry.bones[ i ];\n\n // create new 'Bone' object\n\n bone = new Bone();\n bones.push( bone );\n\n // apply values\n\n bone.name = gbone.name;\n bone.position.fromArray( gbone.pos );\n bone.quaternion.fromArray( gbone.rotq );\n if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl );\n\n }\n\n // second, create bone hierarchy\n\n for ( i = 0, il = this.geometry.bones.length; i < il; i ++ ) {\n\n gbone = this.geometry.bones[ i ];\n\n if ( ( gbone.parent !== - 1 ) && ( gbone.parent !== null ) && ( bones[ gbone.parent ] !== undefined ) ) {\n\n // subsequent bones in the hierarchy\n\n bones[ gbone.parent ].add( bones[ i ] );\n\n } else {\n\n // topmost bone, immediate child of the skinned mesh\n\n this.add( bones[ i ] );\n\n }\n\n }\n\n }\n\n // now the bones are part of the scene graph and children of the skinned mesh.\n // let's update the corresponding matrices\n\n this.updateMatrixWorld( true );\n\n return bones;\n\n },\n\n bind: function ( skeleton, bindMatrix ) {\n\n this.skeleton = skeleton;\n\n if ( bindMatrix === undefined ) {\n\n this.updateMatrixWorld( true );\n\n this.skeleton.calculateInverses();\n\n bindMatrix = this.matrixWorld;\n\n }\n\n this.bindMatrix.copy( bindMatrix );\n this.bindMatrixInverse.getInverse( bindMatrix );\n\n },\n\n pose: function () {\n\n this.skeleton.pose();\n\n },\n\n normalizeSkinWeights: function () {\n\n var scale, i;\n\n if ( this.geometry && this.geometry.isGeometry ) {\n\n for ( i = 0; i < this.geometry.skinWeights.length; i ++ ) {\n\n var sw = this.geometry.skinWeights[ i ];\n\n scale = 1.0 / sw.manhattanLength();\n\n if ( scale !== Infinity ) {\n\n sw.multiplyScalar( scale );\n\n } else {\n\n sw.set( 1, 0, 0, 0 ); // do something reasonable\n\n }\n\n }\n\n } else if ( this.geometry && this.geometry.isBufferGeometry ) {\n\n var vec = new Vector4();\n\n var skinWeight = this.geometry.attributes.skinWeight;\n\n for ( i = 0; i < skinWeight.count; i ++ ) {\n\n vec.x = skinWeight.getX( i );\n vec.y = skinWeight.getY( i );\n vec.z = skinWeight.getZ( i );\n vec.w = skinWeight.getW( i );\n\n scale = 1.0 / vec.manhattanLength();\n\n if ( scale !== Infinity ) {\n\n vec.multiplyScalar( scale );\n\n } else {\n\n vec.set( 1, 0, 0, 0 ); // do something reasonable\n\n }\n\n skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w );\n\n }\n\n }\n\n },\n\n updateMatrixWorld: function ( force ) {\n\n Mesh.prototype.updateMatrixWorld.call( this, force );\n\n if ( this.bindMode === 'attached' ) {\n\n this.bindMatrixInverse.getInverse( this.matrixWorld );\n\n } else if ( this.bindMode === 'detached' ) {\n\n this.bindMatrixInverse.getInverse( this.bindMatrix );\n\n } else {\n\n console.warn( 'THREE.SkinnedMesh: Unrecognized bindMode: ' + this.bindMode );\n\n }\n\n },\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * linewidth: <float>,\n * linecap: \"round\",\n * linejoin: \"round\"\n * }\n */\n\n function LineBasicMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'LineBasicMaterial';\n\n this.color = new Color( 0xffffff );\n\n this.linewidth = 1;\n this.linecap = 'round';\n this.linejoin = 'round';\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n LineBasicMaterial.prototype = Object.create( Material.prototype );\n LineBasicMaterial.prototype.constructor = LineBasicMaterial;\n\n LineBasicMaterial.prototype.isLineBasicMaterial = true;\n\n LineBasicMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.linewidth = source.linewidth;\n this.linecap = source.linecap;\n this.linejoin = source.linejoin;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Line( geometry, material, mode ) {\n\n if ( mode === 1 ) {\n\n console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' );\n return new LineSegments( geometry, material );\n\n }\n\n Object3D.call( this );\n\n this.type = 'Line';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new LineBasicMaterial( { color: Math.random() * 0xffffff } );\n\n }\n\n Line.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Line,\n\n isLine: true,\n\n computeLineDistances: ( function () {\n\n var start = new Vector3();\n var end = new Vector3();\n\n return function computeLineDistances() {\n\n var geometry = this.geometry;\n\n if ( geometry.isBufferGeometry ) {\n\n // we assume non-indexed geometry\n\n if ( geometry.index === null ) {\n\n var positionAttribute = geometry.attributes.position;\n var lineDistances = [ 0 ];\n\n for ( var i = 1, l = positionAttribute.count; i < l; i ++ ) {\n\n start.fromBufferAttribute( positionAttribute, i - 1 );\n end.fromBufferAttribute( positionAttribute, i );\n\n lineDistances[ i ] = lineDistances[ i - 1 ];\n lineDistances[ i ] += start.distanceTo( end );\n\n }\n\n geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) );\n\n } else {\n\n console.warn( 'THREE.Line.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var lineDistances = geometry.lineDistances;\n\n lineDistances[ 0 ] = 0;\n\n for ( var i = 1, l = vertices.length; i < l; i ++ ) {\n\n lineDistances[ i ] = lineDistances[ i - 1 ];\n lineDistances[ i ] += vertices[ i - 1 ].distanceTo( vertices[ i ] );\n\n }\n\n }\n\n return this;\n\n };\n\n }() ),\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n return function raycast( raycaster, intersects ) {\n\n var precision = raycaster.linePrecision;\n var precisionSq = precision * precision;\n\n var geometry = this.geometry;\n var matrixWorld = this.matrixWorld;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n var vStart = new Vector3();\n var vEnd = new Vector3();\n var interSegment = new Vector3();\n var interRay = new Vector3();\n var step = ( this && this.isLineSegments ) ? 2 : 1;\n\n if ( geometry.isBufferGeometry ) {\n\n var index = geometry.index;\n var attributes = geometry.attributes;\n var positions = attributes.position.array;\n\n if ( index !== null ) {\n\n var indices = index.array;\n\n for ( var i = 0, l = indices.length - 1; i < l; i += step ) {\n\n var a = indices[ i ];\n var b = indices[ i + 1 ];\n\n vStart.fromArray( positions, a * 3 );\n vEnd.fromArray( positions, b * 3 );\n\n var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n } else {\n\n for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) {\n\n vStart.fromArray( positions, 3 * i );\n vEnd.fromArray( positions, 3 * i + 3 );\n\n var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var nbVertices = vertices.length;\n\n for ( var i = 0; i < nbVertices - 1; i += step ) {\n\n var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment );\n\n if ( distSq > precisionSq ) continue;\n\n interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation\n\n var distance = raycaster.ray.origin.distanceTo( interRay );\n\n if ( distance < raycaster.near || distance > raycaster.far ) continue;\n\n intersects.push( {\n\n distance: distance,\n // What do we want? intersection point on the ray or on the segment??\n // point: raycaster.ray.at( distance ),\n point: interSegment.clone().applyMatrix4( this.matrixWorld ),\n index: i,\n face: null,\n faceIndex: null,\n object: this\n\n } );\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LineSegments( geometry, material ) {\n\n Line.call( this, geometry, material );\n\n this.type = 'LineSegments';\n\n }\n\n LineSegments.prototype = Object.assign( Object.create( Line.prototype ), {\n\n constructor: LineSegments,\n\n isLineSegments: true,\n\n computeLineDistances: ( function () {\n\n var start = new Vector3();\n var end = new Vector3();\n\n return function computeLineDistances() {\n\n var geometry = this.geometry;\n\n if ( geometry.isBufferGeometry ) {\n\n // we assume non-indexed geometry\n\n if ( geometry.index === null ) {\n\n var positionAttribute = geometry.attributes.position;\n var lineDistances = [];\n\n for ( var i = 0, l = positionAttribute.count; i < l; i += 2 ) {\n\n start.fromBufferAttribute( positionAttribute, i );\n end.fromBufferAttribute( positionAttribute, i + 1 );\n\n lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );\n\n }\n\n geometry.addAttribute( 'lineDistance', new THREE.Float32BufferAttribute( lineDistances, 1 ) );\n\n } else {\n\n console.warn( 'THREE.LineSegments.computeLineDistances(): Computation only possible with non-indexed BufferGeometry.' );\n\n }\n\n } else if ( geometry.isGeometry ) {\n\n var vertices = geometry.vertices;\n var lineDistances = geometry.lineDistances;\n\n for ( var i = 0, l = vertices.length; i < l; i += 2 ) {\n\n start.copy( vertices[ i ] );\n end.copy( vertices[ i + 1 ] );\n\n lineDistances[ i ] = ( i === 0 ) ? 0 : lineDistances[ i - 1 ];\n lineDistances[ i + 1 ] = lineDistances[ i ] + start.distanceTo( end );\n\n }\n\n }\n\n return this;\n\n };\n\n }() )\n\n } );\n\n /**\n * @author mgreter / http://github.com/mgreter\n */\n\n function LineLoop( geometry, material ) {\n\n Line.call( this, geometry, material );\n\n this.type = 'LineLoop';\n\n }\n\n LineLoop.prototype = Object.assign( Object.create( Line.prototype ), {\n\n constructor: LineLoop,\n\n isLineLoop: true,\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n * map: new THREE.Texture( <Image> ),\n *\n * size: <float>,\n * sizeAttenuation: <bool>\n * }\n */\n\n function PointsMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'PointsMaterial';\n\n this.color = new Color( 0xffffff );\n\n this.map = null;\n\n this.size = 1;\n this.sizeAttenuation = true;\n\n this.lights = false;\n\n this.setValues( parameters );\n\n }\n\n PointsMaterial.prototype = Object.create( Material.prototype );\n PointsMaterial.prototype.constructor = PointsMaterial;\n\n PointsMaterial.prototype.isPointsMaterial = true;\n\n PointsMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.size = source.size;\n this.sizeAttenuation = source.sizeAttenuation;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Points( geometry, material ) {\n\n Object3D.call( this );\n\n this.type = 'Points';\n\n this.geometry = geometry !== undefined ? geometry : new BufferGeometry();\n this.material = material !== undefined ? material : new PointsMaterial( { color: Math.random() * 0xffffff } );\n\n }\n\n Points.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Points,\n\n isPoints: true,\n\n raycast: ( function () {\n\n var inverseMatrix = new Matrix4();\n var ray = new Ray();\n var sphere = new Sphere();\n\n return function raycast( raycaster, intersects ) {\n\n var object = this;\n var geometry = this.geometry;\n var matrixWorld = this.matrixWorld;\n var threshold = raycaster.params.Points.threshold;\n\n // Checking boundingSphere distance to ray\n\n if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere();\n\n sphere.copy( geometry.boundingSphere );\n sphere.applyMatrix4( matrixWorld );\n sphere.radius += threshold;\n\n if ( raycaster.ray.intersectsSphere( sphere ) === false ) return;\n\n //\n\n inverseMatrix.getInverse( matrixWorld );\n ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix );\n\n var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 );\n var localThresholdSq = localThreshold * localThreshold;\n var position = new Vector3();\n var intersectPoint = new Vector3();\n\n function testPoint( point, index ) {\n\n var rayPointDistanceSq = ray.distanceSqToPoint( point );\n\n if ( rayPointDistanceSq < localThresholdSq ) {\n\n ray.closestPointToPoint( point, intersectPoint );\n intersectPoint.applyMatrix4( matrixWorld );\n\n var distance = raycaster.ray.origin.distanceTo( intersectPoint );\n\n if ( distance < raycaster.near || distance > raycaster.far ) return;\n\n intersects.push( {\n\n distance: distance,\n distanceToRay: Math.sqrt( rayPointDistanceSq ),\n point: intersectPoint.clone(),\n index: index,\n face: null,\n object: object\n\n } );\n\n }\n\n }\n\n if ( geometry.isBufferGeometry ) {\n\n var index = geometry.index;\n var attributes = geometry.attributes;\n var positions = attributes.position.array;\n\n if ( index !== null ) {\n\n var indices = index.array;\n\n for ( var i = 0, il = indices.length; i < il; i ++ ) {\n\n var a = indices[ i ];\n\n position.fromArray( positions, a * 3 );\n\n testPoint( position, a );\n\n }\n\n } else {\n\n for ( var i = 0, l = positions.length / 3; i < l; i ++ ) {\n\n position.fromArray( positions, i * 3 );\n\n testPoint( position, i );\n\n }\n\n }\n\n } else {\n\n var vertices = geometry.vertices;\n\n for ( var i = 0, l = vertices.length; i < l; i ++ ) {\n\n testPoint( vertices[ i ], i );\n\n }\n\n }\n\n };\n\n }() ),\n\n clone: function () {\n\n return new this.constructor( this.geometry, this.material ).copy( this );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Group() {\n\n Object3D.call( this );\n\n this.type = 'Group';\n\n }\n\n Group.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Group,\n\n isGroup: true\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function VideoTexture( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) {\n\n Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.generateMipmaps = false;\n\n }\n\n VideoTexture.prototype = Object.assign( Object.create( Texture.prototype ), {\n\n constructor: VideoTexture,\n\n isVideoTexture: true,\n\n update: function () {\n\n var video = this.image;\n\n if ( video.readyState >= video.HAVE_CURRENT_DATA ) {\n\n this.needsUpdate = true;\n\n }\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function CompressedTexture( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, encoding ) {\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy, encoding );\n\n this.image = { width: width, height: height };\n this.mipmaps = mipmaps;\n\n // no flipping for cube textures\n // (also flipping doesn't work for compressed textures )\n\n this.flipY = false;\n\n // can't generate mipmaps for compressed textures\n // mips must be embedded in DDS files\n\n this.generateMipmaps = false;\n\n }\n\n CompressedTexture.prototype = Object.create( Texture.prototype );\n CompressedTexture.prototype.constructor = CompressedTexture;\n\n CompressedTexture.prototype.isCompressedTexture = true;\n\n /**\n * @author Matt DesLauriers / @mattdesl\n * @author atix / arthursilber.de\n */\n\n function DepthTexture( width, height, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy, format ) {\n\n format = format !== undefined ? format : DepthFormat;\n\n if ( format !== DepthFormat && format !== DepthStencilFormat ) {\n\n throw new Error( 'DepthTexture format must be either THREE.DepthFormat or THREE.DepthStencilFormat' );\n\n }\n\n if ( type === undefined && format === DepthFormat ) type = UnsignedShortType;\n if ( type === undefined && format === DepthStencilFormat ) type = UnsignedInt248Type;\n\n Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy );\n\n this.image = { width: width, height: height };\n\n this.magFilter = magFilter !== undefined ? magFilter : NearestFilter;\n this.minFilter = minFilter !== undefined ? minFilter : NearestFilter;\n\n this.flipY = false;\n this.generateMipmaps = false;\n\n }\n\n DepthTexture.prototype = Object.create( Texture.prototype );\n DepthTexture.prototype.constructor = DepthTexture;\n DepthTexture.prototype.isDepthTexture = true;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function WireframeGeometry( geometry ) {\n\n BufferGeometry.call( this );\n\n this.type = 'WireframeGeometry';\n\n // buffer\n\n var vertices = [];\n\n // helper variables\n\n var i, j, l, o, ol;\n var edge = [ 0, 0 ], edges = {}, e, edge1, edge2;\n var key, keys = [ 'a', 'b', 'c' ];\n var vertex;\n\n // different logic for Geometry and BufferGeometry\n\n if ( geometry && geometry.isGeometry ) {\n\n // create a data structure that contains all edges without duplicates\n\n var faces = geometry.faces;\n\n for ( i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( j = 0; j < 3; j ++ ) {\n\n edge1 = face[ keys[ j ] ];\n edge2 = face[ keys[ ( j + 1 ) % 3 ] ];\n edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n e = edges[ key ];\n\n vertex = geometry.vertices[ e.index1 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex = geometry.vertices[ e.index2 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n } else if ( geometry && geometry.isBufferGeometry ) {\n\n var position, indices, groups;\n var group, start, count;\n var index1, index2;\n\n vertex = new Vector3();\n\n if ( geometry.index !== null ) {\n\n // indexed BufferGeometry\n\n position = geometry.attributes.position;\n indices = geometry.index;\n groups = geometry.groups;\n\n if ( groups.length === 0 ) {\n\n groups = [ { start: 0, count: indices.count, materialIndex: 0 } ];\n\n }\n\n // create a data structure that contains all eges without duplicates\n\n for ( o = 0, ol = groups.length; o < ol; ++ o ) {\n\n group = groups[ o ];\n\n start = group.start;\n count = group.count;\n\n for ( i = start, l = ( start + count ); i < l; i += 3 ) {\n\n for ( j = 0; j < 3; j ++ ) {\n\n edge1 = indices.getX( i + j );\n edge2 = indices.getX( i + ( j + 1 ) % 3 );\n edge[ 0 ] = Math.min( edge1, edge2 ); // sorting prevents duplicates\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ] };\n\n }\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n e = edges[ key ];\n\n vertex.fromBufferAttribute( position, e.index1 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex.fromBufferAttribute( position, e.index2 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n } else {\n\n // non-indexed BufferGeometry\n\n position = geometry.attributes.position;\n\n for ( i = 0, l = ( position.count / 3 ); i < l; i ++ ) {\n\n for ( j = 0; j < 3; j ++ ) {\n\n // three edges per triangle, an edge is represented as (index1, index2)\n // e.g. the first triangle has the following edges: (0,1),(1,2),(2,0)\n\n index1 = 3 * i + j;\n vertex.fromBufferAttribute( position, index1 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n index2 = 3 * i + ( ( j + 1 ) % 3 );\n vertex.fromBufferAttribute( position, index2 );\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n }\n\n }\n\n // build geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n }\n\n WireframeGeometry.prototype = Object.create( BufferGeometry.prototype );\n WireframeGeometry.prototype.constructor = WireframeGeometry;\n\n /**\n * @author zz85 / https://github.com/zz85\n * @author Mugen87 / https://github.com/Mugen87\n *\n * Parametric Surfaces Geometry\n * based on the brilliant article by @prideout http://prideout.net/blog/?p=44\n */\n\n // ParametricGeometry\n\n function ParametricGeometry( func, slices, stacks ) {\n\n Geometry.call( this );\n\n this.type = 'ParametricGeometry';\n\n this.parameters = {\n func: func,\n slices: slices,\n stacks: stacks\n };\n\n this.fromBufferGeometry( new ParametricBufferGeometry( func, slices, stacks ) );\n this.mergeVertices();\n\n }\n\n ParametricGeometry.prototype = Object.create( Geometry.prototype );\n ParametricGeometry.prototype.constructor = ParametricGeometry;\n\n // ParametricBufferGeometry\n\n function ParametricBufferGeometry( func, slices, stacks ) {\n\n BufferGeometry.call( this );\n\n this.type = 'ParametricBufferGeometry';\n\n this.parameters = {\n func: func,\n slices: slices,\n stacks: stacks\n };\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n var EPS = 0.00001;\n\n var normal = new Vector3();\n\n var p0 = new Vector3(), p1 = new Vector3();\n var pu = new Vector3(), pv = new Vector3();\n\n var i, j;\n\n // generate vertices, normals and uvs\n\n var sliceCount = slices + 1;\n\n for ( i = 0; i <= stacks; i ++ ) {\n\n var v = i / stacks;\n\n for ( j = 0; j <= slices; j ++ ) {\n\n var u = j / slices;\n\n // vertex\n\n func( u, v, p0 );\n vertices.push( p0.x, p0.y, p0.z );\n\n // normal\n\n // approximate tangent vectors via finite differences\n\n if ( u - EPS >= 0 ) {\n\n func( u - EPS, v, p1 );\n pu.subVectors( p0, p1 );\n\n } else {\n\n func( u + EPS, v, p1 );\n pu.subVectors( p1, p0 );\n\n }\n\n if ( v - EPS >= 0 ) {\n\n func( u, v - EPS, p1 );\n pv.subVectors( p0, p1 );\n\n } else {\n\n func( u, v + EPS, p1 );\n pv.subVectors( p1, p0 );\n\n }\n\n // cross product of tangent vectors returns surface normal\n\n normal.crossVectors( pu, pv ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, v );\n\n }\n\n }\n\n // generate indices\n\n for ( i = 0; i < stacks; i ++ ) {\n\n for ( j = 0; j < slices; j ++ ) {\n\n var a = i * sliceCount + j;\n var b = i * sliceCount + j + 1;\n var c = ( i + 1 ) * sliceCount + j + 1;\n var d = ( i + 1 ) * sliceCount + j;\n\n // faces one and two\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n ParametricBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ParametricBufferGeometry.prototype.constructor = ParametricBufferGeometry;\n\n /**\n * @author clockworkgeek / https://github.com/clockworkgeek\n * @author timothypratley / https://github.com/timothypratley\n * @author WestLangley / http://github.com/WestLangley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // PolyhedronGeometry\n\n function PolyhedronGeometry( vertices, indices, radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'PolyhedronGeometry';\n\n this.parameters = {\n vertices: vertices,\n indices: indices,\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new PolyhedronBufferGeometry( vertices, indices, radius, detail ) );\n this.mergeVertices();\n\n }\n\n PolyhedronGeometry.prototype = Object.create( Geometry.prototype );\n PolyhedronGeometry.prototype.constructor = PolyhedronGeometry;\n\n // PolyhedronBufferGeometry\n\n function PolyhedronBufferGeometry( vertices, indices, radius, detail ) {\n\n BufferGeometry.call( this );\n\n this.type = 'PolyhedronBufferGeometry';\n\n this.parameters = {\n vertices: vertices,\n indices: indices,\n radius: radius,\n detail: detail\n };\n\n radius = radius || 1;\n detail = detail || 0;\n\n // default buffer data\n\n var vertexBuffer = [];\n var uvBuffer = [];\n\n // the subdivision creates the vertex buffer data\n\n subdivide( detail );\n\n // all vertices should lie on a conceptual sphere with a given radius\n\n appplyRadius( radius );\n\n // finally, create the uv data\n\n generateUVs();\n\n // build non-indexed geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertexBuffer, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( vertexBuffer.slice(), 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvBuffer, 2 ) );\n\n if ( detail === 0 ) {\n\n this.computeVertexNormals(); // flat normals\n\n } else {\n\n this.normalizeNormals(); // smooth normals\n\n }\n\n // helper functions\n\n function subdivide( detail ) {\n\n var a = new Vector3();\n var b = new Vector3();\n var c = new Vector3();\n\n // iterate over all faces and apply a subdivison with the given detail value\n\n for ( var i = 0; i < indices.length; i += 3 ) {\n\n // get the vertices of the face\n\n getVertexByIndex( indices[ i + 0 ], a );\n getVertexByIndex( indices[ i + 1 ], b );\n getVertexByIndex( indices[ i + 2 ], c );\n\n // perform subdivision\n\n subdivideFace( a, b, c, detail );\n\n }\n\n }\n\n function subdivideFace( a, b, c, detail ) {\n\n var cols = Math.pow( 2, detail );\n\n // we use this multidimensional array as a data structure for creating the subdivision\n\n var v = [];\n\n var i, j;\n\n // construct all of the vertices for this subdivision\n\n for ( i = 0; i <= cols; i ++ ) {\n\n v[ i ] = [];\n\n var aj = a.clone().lerp( c, i / cols );\n var bj = b.clone().lerp( c, i / cols );\n\n var rows = cols - i;\n\n for ( j = 0; j <= rows; j ++ ) {\n\n if ( j === 0 && i === cols ) {\n\n v[ i ][ j ] = aj;\n\n } else {\n\n v[ i ][ j ] = aj.clone().lerp( bj, j / rows );\n\n }\n\n }\n\n }\n\n // construct all of the faces\n\n for ( i = 0; i < cols; i ++ ) {\n\n for ( j = 0; j < 2 * ( cols - i ) - 1; j ++ ) {\n\n var k = Math.floor( j / 2 );\n\n if ( j % 2 === 0 ) {\n\n pushVertex( v[ i ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k ] );\n pushVertex( v[ i ][ k ] );\n\n } else {\n\n pushVertex( v[ i ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k + 1 ] );\n pushVertex( v[ i + 1 ][ k ] );\n\n }\n\n }\n\n }\n\n }\n\n function appplyRadius( radius ) {\n\n var vertex = new Vector3();\n\n // iterate over the entire buffer and apply the radius to each vertex\n\n for ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n vertex.x = vertexBuffer[ i + 0 ];\n vertex.y = vertexBuffer[ i + 1 ];\n vertex.z = vertexBuffer[ i + 2 ];\n\n vertex.normalize().multiplyScalar( radius );\n\n vertexBuffer[ i + 0 ] = vertex.x;\n vertexBuffer[ i + 1 ] = vertex.y;\n vertexBuffer[ i + 2 ] = vertex.z;\n\n }\n\n }\n\n function generateUVs() {\n\n var vertex = new Vector3();\n\n for ( var i = 0; i < vertexBuffer.length; i += 3 ) {\n\n vertex.x = vertexBuffer[ i + 0 ];\n vertex.y = vertexBuffer[ i + 1 ];\n vertex.z = vertexBuffer[ i + 2 ];\n\n var u = azimuth( vertex ) / 2 / Math.PI + 0.5;\n var v = inclination( vertex ) / Math.PI + 0.5;\n uvBuffer.push( u, 1 - v );\n\n }\n\n correctUVs();\n\n correctSeam();\n\n }\n\n function correctSeam() {\n\n // handle case when face straddles the seam, see #3269\n\n for ( var i = 0; i < uvBuffer.length; i += 6 ) {\n\n // uv data of a single face\n\n var x0 = uvBuffer[ i + 0 ];\n var x1 = uvBuffer[ i + 2 ];\n var x2 = uvBuffer[ i + 4 ];\n\n var max = Math.max( x0, x1, x2 );\n var min = Math.min( x0, x1, x2 );\n\n // 0.9 is somewhat arbitrary\n\n if ( max > 0.9 && min < 0.1 ) {\n\n if ( x0 < 0.2 ) uvBuffer[ i + 0 ] += 1;\n if ( x1 < 0.2 ) uvBuffer[ i + 2 ] += 1;\n if ( x2 < 0.2 ) uvBuffer[ i + 4 ] += 1;\n\n }\n\n }\n\n }\n\n function pushVertex( vertex ) {\n\n vertexBuffer.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n function getVertexByIndex( index, vertex ) {\n\n var stride = index * 3;\n\n vertex.x = vertices[ stride + 0 ];\n vertex.y = vertices[ stride + 1 ];\n vertex.z = vertices[ stride + 2 ];\n\n }\n\n function correctUVs() {\n\n var a = new Vector3();\n var b = new Vector3();\n var c = new Vector3();\n\n var centroid = new Vector3();\n\n var uvA = new Vector2();\n var uvB = new Vector2();\n var uvC = new Vector2();\n\n for ( var i = 0, j = 0; i < vertexBuffer.length; i += 9, j += 6 ) {\n\n a.set( vertexBuffer[ i + 0 ], vertexBuffer[ i + 1 ], vertexBuffer[ i + 2 ] );\n b.set( vertexBuffer[ i + 3 ], vertexBuffer[ i + 4 ], vertexBuffer[ i + 5 ] );\n c.set( vertexBuffer[ i + 6 ], vertexBuffer[ i + 7 ], vertexBuffer[ i + 8 ] );\n\n uvA.set( uvBuffer[ j + 0 ], uvBuffer[ j + 1 ] );\n uvB.set( uvBuffer[ j + 2 ], uvBuffer[ j + 3 ] );\n uvC.set( uvBuffer[ j + 4 ], uvBuffer[ j + 5 ] );\n\n centroid.copy( a ).add( b ).add( c ).divideScalar( 3 );\n\n var azi = azimuth( centroid );\n\n correctUV( uvA, j + 0, a, azi );\n correctUV( uvB, j + 2, b, azi );\n correctUV( uvC, j + 4, c, azi );\n\n }\n\n }\n\n function correctUV( uv, stride, vector, azimuth ) {\n\n if ( ( azimuth < 0 ) && ( uv.x === 1 ) ) {\n\n uvBuffer[ stride ] = uv.x - 1;\n\n }\n\n if ( ( vector.x === 0 ) && ( vector.z === 0 ) ) {\n\n uvBuffer[ stride ] = azimuth / 2 / Math.PI + 0.5;\n\n }\n\n }\n\n // Angle around the Y axis, counter-clockwise when looking from above.\n\n function azimuth( vector ) {\n\n return Math.atan2( vector.z, - vector.x );\n\n }\n\n\n // Angle above the XZ plane.\n\n function inclination( vector ) {\n\n return Math.atan2( - vector.y, Math.sqrt( ( vector.x * vector.x ) + ( vector.z * vector.z ) ) );\n\n }\n\n }\n\n PolyhedronBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n PolyhedronBufferGeometry.prototype.constructor = PolyhedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // TetrahedronGeometry\n\n function TetrahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'TetrahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new TetrahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n TetrahedronGeometry.prototype = Object.create( Geometry.prototype );\n TetrahedronGeometry.prototype.constructor = TetrahedronGeometry;\n\n // TetrahedronBufferGeometry\n\n function TetrahedronBufferGeometry( radius, detail ) {\n\n var vertices = [\n 1, 1, 1, - 1, - 1, 1, - 1, 1, - 1, 1, - 1, - 1\n ];\n\n var indices = [\n 2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'TetrahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n TetrahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n TetrahedronBufferGeometry.prototype.constructor = TetrahedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // OctahedronGeometry\n\n function OctahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'OctahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new OctahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n OctahedronGeometry.prototype = Object.create( Geometry.prototype );\n OctahedronGeometry.prototype.constructor = OctahedronGeometry;\n\n // OctahedronBufferGeometry\n\n function OctahedronBufferGeometry( radius, detail ) {\n\n var vertices = [\n 1, 0, 0, - 1, 0, 0, 0, 1, 0,\n 0, - 1, 0, 0, 0, 1, 0, 0, - 1\n ];\n\n var indices = [\n 0, 2, 4, 0, 4, 3, 0, 3, 5,\n 0, 5, 2, 1, 2, 5, 1, 5, 3,\n 1, 3, 4, 1, 4, 2\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'OctahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n OctahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n OctahedronBufferGeometry.prototype.constructor = OctahedronBufferGeometry;\n\n /**\n * @author timothypratley / https://github.com/timothypratley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // IcosahedronGeometry\n\n function IcosahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'IcosahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new IcosahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n IcosahedronGeometry.prototype = Object.create( Geometry.prototype );\n IcosahedronGeometry.prototype.constructor = IcosahedronGeometry;\n\n // IcosahedronBufferGeometry\n\n function IcosahedronBufferGeometry( radius, detail ) {\n\n var t = ( 1 + Math.sqrt( 5 ) ) / 2;\n\n var vertices = [\n - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t, 0,\n 0, - 1, t, 0, 1, t, 0, - 1, - t, 0, 1, - t,\n t, 0, - 1, t, 0, 1, - t, 0, - 1, - t, 0, 1\n ];\n\n var indices = [\n 0, 11, 5, 0, 5, 1, 0, 1, 7, 0, 7, 10, 0, 10, 11,\n 1, 5, 9, 5, 11, 4, 11, 10, 2, 10, 7, 6, 7, 1, 8,\n 3, 9, 4, 3, 4, 2, 3, 2, 6, 3, 6, 8, 3, 8, 9,\n 4, 9, 5, 2, 4, 11, 6, 2, 10, 8, 6, 7, 9, 8, 1\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'IcosahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n IcosahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n IcosahedronBufferGeometry.prototype.constructor = IcosahedronBufferGeometry;\n\n /**\n * @author Abe Pazos / https://hamoid.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // DodecahedronGeometry\n\n function DodecahedronGeometry( radius, detail ) {\n\n Geometry.call( this );\n\n this.type = 'DodecahedronGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n this.fromBufferGeometry( new DodecahedronBufferGeometry( radius, detail ) );\n this.mergeVertices();\n\n }\n\n DodecahedronGeometry.prototype = Object.create( Geometry.prototype );\n DodecahedronGeometry.prototype.constructor = DodecahedronGeometry;\n\n // DodecahedronBufferGeometry\n\n function DodecahedronBufferGeometry( radius, detail ) {\n\n var t = ( 1 + Math.sqrt( 5 ) ) / 2;\n var r = 1 / t;\n\n var vertices = [\n\n // (±1, ±1, ±1)\n - 1, - 1, - 1, - 1, - 1, 1,\n - 1, 1, - 1, - 1, 1, 1,\n 1, - 1, - 1, 1, - 1, 1,\n 1, 1, - 1, 1, 1, 1,\n\n // (0, ±1/φ, ±φ)\n 0, - r, - t, 0, - r, t,\n 0, r, - t, 0, r, t,\n\n // (±1/φ, ±φ, 0)\n - r, - t, 0, - r, t, 0,\n r, - t, 0, r, t, 0,\n\n // (±φ, 0, ±1/φ)\n - t, 0, - r, t, 0, - r,\n - t, 0, r, t, 0, r\n ];\n\n var indices = [\n 3, 11, 7, 3, 7, 15, 3, 15, 13,\n 7, 19, 17, 7, 17, 6, 7, 6, 15,\n 17, 4, 8, 17, 8, 10, 17, 10, 6,\n 8, 0, 16, 8, 16, 2, 8, 2, 10,\n 0, 12, 1, 0, 1, 18, 0, 18, 16,\n 6, 10, 2, 6, 2, 13, 6, 13, 15,\n 2, 16, 18, 2, 18, 3, 2, 3, 13,\n 18, 1, 9, 18, 9, 11, 18, 11, 3,\n 4, 14, 12, 4, 12, 0, 4, 0, 8,\n 11, 9, 5, 11, 5, 19, 11, 19, 7,\n 19, 5, 14, 19, 14, 4, 19, 4, 17,\n 1, 12, 14, 1, 14, 5, 1, 5, 9\n ];\n\n PolyhedronBufferGeometry.call( this, vertices, indices, radius, detail );\n\n this.type = 'DodecahedronBufferGeometry';\n\n this.parameters = {\n radius: radius,\n detail: detail\n };\n\n }\n\n DodecahedronBufferGeometry.prototype = Object.create( PolyhedronBufferGeometry.prototype );\n DodecahedronBufferGeometry.prototype.constructor = DodecahedronBufferGeometry;\n\n /**\n * @author oosmoxiecode / https://github.com/oosmoxiecode\n * @author WestLangley / https://github.com/WestLangley\n * @author zz85 / https://github.com/zz85\n * @author miningold / https://github.com/miningold\n * @author jonobr1 / https://github.com/jonobr1\n * @author Mugen87 / https://github.com/Mugen87\n *\n */\n\n // TubeGeometry\n\n function TubeGeometry( path, tubularSegments, radius, radialSegments, closed, taper ) {\n\n Geometry.call( this );\n\n this.type = 'TubeGeometry';\n\n this.parameters = {\n path: path,\n tubularSegments: tubularSegments,\n radius: radius,\n radialSegments: radialSegments,\n closed: closed\n };\n\n if ( taper !== undefined ) console.warn( 'THREE.TubeGeometry: taper has been removed.' );\n\n var bufferGeometry = new TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed );\n\n // expose internals\n\n this.tangents = bufferGeometry.tangents;\n this.normals = bufferGeometry.normals;\n this.binormals = bufferGeometry.binormals;\n\n // create geometry\n\n this.fromBufferGeometry( bufferGeometry );\n this.mergeVertices();\n\n }\n\n TubeGeometry.prototype = Object.create( Geometry.prototype );\n TubeGeometry.prototype.constructor = TubeGeometry;\n\n // TubeBufferGeometry\n\n function TubeBufferGeometry( path, tubularSegments, radius, radialSegments, closed ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TubeBufferGeometry';\n\n this.parameters = {\n path: path,\n tubularSegments: tubularSegments,\n radius: radius,\n radialSegments: radialSegments,\n closed: closed\n };\n\n tubularSegments = tubularSegments || 64;\n radius = radius || 1;\n radialSegments = radialSegments || 8;\n closed = closed || false;\n\n var frames = path.computeFrenetFrames( tubularSegments, closed );\n\n // expose internals\n\n this.tangents = frames.tangents;\n this.normals = frames.normals;\n this.binormals = frames.binormals;\n\n // helper variables\n\n var vertex = new Vector3();\n var normal = new Vector3();\n var uv = new Vector2();\n var P = new Vector3();\n\n var i, j;\n\n // buffer\n\n var vertices = [];\n var normals = [];\n var uvs = [];\n var indices = [];\n\n // create buffer data\n\n generateBufferData();\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // functions\n\n function generateBufferData() {\n\n for ( i = 0; i < tubularSegments; i ++ ) {\n\n generateSegment( i );\n\n }\n\n // if the geometry is not closed, generate the last row of vertices and normals\n // at the regular position on the given path\n //\n // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)\n\n generateSegment( ( closed === false ) ? tubularSegments : 0 );\n\n // uvs are generated in a separate function.\n // this makes it easy compute correct values for closed geometries\n\n generateUVs();\n\n // finally create faces\n\n generateIndices();\n\n }\n\n function generateSegment( i ) {\n\n // we use getPointAt to sample evenly distributed points from the given path\n\n P = path.getPointAt( i / tubularSegments, P );\n\n // retrieve corresponding normal and binormal\n\n var N = frames.normals[ i ];\n var B = frames.binormals[ i ];\n\n // generate normals and vertices for the current segment\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n var v = j / radialSegments * Math.PI * 2;\n\n var sin = Math.sin( v );\n var cos = - Math.cos( v );\n\n // normal\n\n normal.x = ( cos * N.x + sin * B.x );\n normal.y = ( cos * N.y + sin * B.y );\n normal.z = ( cos * N.z + sin * B.z );\n normal.normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // vertex\n\n vertex.x = P.x + radius * normal.x;\n vertex.y = P.y + radius * normal.y;\n vertex.z = P.z + radius * normal.z;\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n function generateIndices() {\n\n for ( j = 1; j <= tubularSegments; j ++ ) {\n\n for ( i = 1; i <= radialSegments; i ++ ) {\n\n var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n var b = ( radialSegments + 1 ) * j + ( i - 1 );\n var c = ( radialSegments + 1 ) * j + i;\n var d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n }\n\n function generateUVs() {\n\n for ( i = 0; i <= tubularSegments; i ++ ) {\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n uv.x = i / tubularSegments;\n uv.y = j / radialSegments;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n }\n\n }\n\n }\n\n TubeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TubeBufferGeometry.prototype.constructor = TubeBufferGeometry;\n\n /**\n * @author oosmoxiecode\n * @author Mugen87 / https://github.com/Mugen87\n *\n * based on http://www.blackpawn.com/texts/pqtorus/\n */\n\n // TorusKnotGeometry\n\n function TorusKnotGeometry( radius, tube, tubularSegments, radialSegments, p, q, heightScale ) {\n\n Geometry.call( this );\n\n this.type = 'TorusKnotGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n tubularSegments: tubularSegments,\n radialSegments: radialSegments,\n p: p,\n q: q\n };\n\n if ( heightScale !== undefined ) console.warn( 'THREE.TorusKnotGeometry: heightScale has been deprecated. Use .scale( x, y, z ) instead.' );\n\n this.fromBufferGeometry( new TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) );\n this.mergeVertices();\n\n }\n\n TorusKnotGeometry.prototype = Object.create( Geometry.prototype );\n TorusKnotGeometry.prototype.constructor = TorusKnotGeometry;\n\n // TorusKnotBufferGeometry\n\n function TorusKnotBufferGeometry( radius, tube, tubularSegments, radialSegments, p, q ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TorusKnotBufferGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n tubularSegments: tubularSegments,\n radialSegments: radialSegments,\n p: p,\n q: q\n };\n\n radius = radius || 1;\n tube = tube || 0.4;\n tubularSegments = Math.floor( tubularSegments ) || 64;\n radialSegments = Math.floor( radialSegments ) || 8;\n p = p || 2;\n q = q || 3;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var i, j;\n\n var vertex = new Vector3();\n var normal = new Vector3();\n\n var P1 = new Vector3();\n var P2 = new Vector3();\n\n var B = new Vector3();\n var T = new Vector3();\n var N = new Vector3();\n\n // generate vertices, normals and uvs\n\n for ( i = 0; i <= tubularSegments; ++ i ) {\n\n // the radian \"u\" is used to calculate the position on the torus curve of the current tubular segement\n\n var u = i / tubularSegments * p * Math.PI * 2;\n\n // now we calculate two points. P1 is our current position on the curve, P2 is a little farther ahead.\n // these points are used to create a special \"coordinate space\", which is necessary to calculate the correct vertex positions\n\n calculatePositionOnCurve( u, p, q, radius, P1 );\n calculatePositionOnCurve( u + 0.01, p, q, radius, P2 );\n\n // calculate orthonormal basis\n\n T.subVectors( P2, P1 );\n N.addVectors( P2, P1 );\n B.crossVectors( T, N );\n N.crossVectors( B, T );\n\n // normalize B, N. T can be ignored, we don't use it\n\n B.normalize();\n N.normalize();\n\n for ( j = 0; j <= radialSegments; ++ j ) {\n\n // now calculate the vertices. they are nothing more than an extrusion of the torus curve.\n // because we extrude a shape in the xy-plane, there is no need to calculate a z-value.\n\n var v = j / radialSegments * Math.PI * 2;\n var cx = - tube * Math.cos( v );\n var cy = tube * Math.sin( v );\n\n // now calculate the final vertex position.\n // first we orient the extrusion with our basis vectos, then we add it to the current position on the curve\n\n vertex.x = P1.x + ( cx * N.x + cy * B.x );\n vertex.y = P1.y + ( cx * N.y + cy * B.y );\n vertex.z = P1.z + ( cx * N.z + cy * B.z );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal (P1 is always the center/origin of the extrusion, thus we can use it to calculate the normal)\n\n normal.subVectors( vertex, P1 ).normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( i / tubularSegments );\n uvs.push( j / radialSegments );\n\n }\n\n }\n\n // generate indices\n\n for ( j = 1; j <= tubularSegments; j ++ ) {\n\n for ( i = 1; i <= radialSegments; i ++ ) {\n\n // indices\n\n var a = ( radialSegments + 1 ) * ( j - 1 ) + ( i - 1 );\n var b = ( radialSegments + 1 ) * j + ( i - 1 );\n var c = ( radialSegments + 1 ) * j + i;\n var d = ( radialSegments + 1 ) * ( j - 1 ) + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // this function calculates the current position on the torus curve\n\n function calculatePositionOnCurve( u, p, q, radius, position ) {\n\n var cu = Math.cos( u );\n var su = Math.sin( u );\n var quOverP = q / p * u;\n var cs = Math.cos( quOverP );\n\n position.x = radius * ( 2 + cs ) * 0.5 * cu;\n position.y = radius * ( 2 + cs ) * su * 0.5;\n position.z = radius * Math.sin( quOverP ) * 0.5;\n\n }\n\n }\n\n TorusKnotBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TorusKnotBufferGeometry.prototype.constructor = TorusKnotBufferGeometry;\n\n /**\n * @author oosmoxiecode\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // TorusGeometry\n\n function TorusGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n Geometry.call( this );\n\n this.type = 'TorusGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n radialSegments: radialSegments,\n tubularSegments: tubularSegments,\n arc: arc\n };\n\n this.fromBufferGeometry( new TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) );\n this.mergeVertices();\n\n }\n\n TorusGeometry.prototype = Object.create( Geometry.prototype );\n TorusGeometry.prototype.constructor = TorusGeometry;\n\n // TorusBufferGeometry\n\n function TorusBufferGeometry( radius, tube, radialSegments, tubularSegments, arc ) {\n\n BufferGeometry.call( this );\n\n this.type = 'TorusBufferGeometry';\n\n this.parameters = {\n radius: radius,\n tube: tube,\n radialSegments: radialSegments,\n tubularSegments: tubularSegments,\n arc: arc\n };\n\n radius = radius || 1;\n tube = tube || 0.4;\n radialSegments = Math.floor( radialSegments ) || 8;\n tubularSegments = Math.floor( tubularSegments ) || 6;\n arc = arc || Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var center = new Vector3();\n var vertex = new Vector3();\n var normal = new Vector3();\n\n var j, i;\n\n // generate vertices, normals and uvs\n\n for ( j = 0; j <= radialSegments; j ++ ) {\n\n for ( i = 0; i <= tubularSegments; i ++ ) {\n\n var u = i / tubularSegments * arc;\n var v = j / radialSegments * Math.PI * 2;\n\n // vertex\n\n vertex.x = ( radius + tube * Math.cos( v ) ) * Math.cos( u );\n vertex.y = ( radius + tube * Math.cos( v ) ) * Math.sin( u );\n vertex.z = tube * Math.sin( v );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n center.x = radius * Math.cos( u );\n center.y = radius * Math.sin( u );\n normal.subVectors( vertex, center ).normalize();\n\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( i / tubularSegments );\n uvs.push( j / radialSegments );\n\n }\n\n }\n\n // generate indices\n\n for ( j = 1; j <= radialSegments; j ++ ) {\n\n for ( i = 1; i <= tubularSegments; i ++ ) {\n\n // indices\n\n var a = ( tubularSegments + 1 ) * j + i - 1;\n var b = ( tubularSegments + 1 ) * ( j - 1 ) + i - 1;\n var c = ( tubularSegments + 1 ) * ( j - 1 ) + i;\n var d = ( tubularSegments + 1 ) * j + i;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n TorusBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n TorusBufferGeometry.prototype.constructor = TorusBufferGeometry;\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n * Port from https://github.com/mapbox/earcut (v2.1.2)\n */\n\n var Earcut = {\n\n triangulate: function ( data, holeIndices, dim ) {\n\n dim = dim || 2;\n\n var hasHoles = holeIndices && holeIndices.length,\n outerLen = hasHoles ? holeIndices[ 0 ] * dim : data.length,\n outerNode = linkedList( data, 0, outerLen, dim, true ),\n triangles = [];\n\n if ( ! outerNode ) return triangles;\n\n var minX, minY, maxX, maxY, x, y, invSize;\n\n if ( hasHoles ) outerNode = eliminateHoles( data, holeIndices, outerNode, dim );\n\n // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox\n\n if ( data.length > 80 * dim ) {\n\n minX = maxX = data[ 0 ];\n minY = maxY = data[ 1 ];\n\n for ( var i = dim; i < outerLen; i += dim ) {\n\n x = data[ i ];\n y = data[ i + 1 ];\n if ( x < minX ) minX = x;\n if ( y < minY ) minY = y;\n if ( x > maxX ) maxX = x;\n if ( y > maxY ) maxY = y;\n\n }\n\n // minX, minY and invSize are later used to transform coords into integers for z-order calculation\n\n invSize = Math.max( maxX - minX, maxY - minY );\n invSize = invSize !== 0 ? 1 / invSize : 0;\n\n }\n\n earcutLinked( outerNode, triangles, dim, minX, minY, invSize );\n\n return triangles;\n\n }\n\n };\n\n // create a circular doubly linked list from polygon points in the specified winding order\n\n function linkedList( data, start, end, dim, clockwise ) {\n\n var i, last;\n\n if ( clockwise === ( signedArea( data, start, end, dim ) > 0 ) ) {\n\n for ( i = start; i < end; i += dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n } else {\n\n for ( i = end - dim; i >= start; i -= dim ) last = insertNode( i, data[ i ], data[ i + 1 ], last );\n\n }\n\n if ( last && equals( last, last.next ) ) {\n\n removeNode( last );\n last = last.next;\n\n }\n\n return last;\n\n }\n\n // eliminate colinear or duplicate points\n\n function filterPoints( start, end ) {\n\n if ( ! start ) return start;\n if ( ! end ) end = start;\n\n var p = start, again;\n\n do {\n\n again = false;\n\n if ( ! p.steiner && ( equals( p, p.next ) || area( p.prev, p, p.next ) === 0 ) ) {\n\n removeNode( p );\n p = end = p.prev;\n if ( p === p.next ) break;\n again = true;\n\n } else {\n\n p = p.next;\n\n }\n\n } while ( again || p !== end );\n\n return end;\n\n }\n\n // main ear slicing loop which triangulates a polygon (given as a linked list)\n\n function earcutLinked( ear, triangles, dim, minX, minY, invSize, pass ) {\n\n if ( ! ear ) return;\n\n // interlink polygon nodes in z-order\n\n if ( ! pass && invSize ) indexCurve( ear, minX, minY, invSize );\n\n var stop = ear, prev, next;\n\n // iterate through ears, slicing them one by one\n\n while ( ear.prev !== ear.next ) {\n\n prev = ear.prev;\n next = ear.next;\n\n if ( invSize ? isEarHashed( ear, minX, minY, invSize ) : isEar( ear ) ) {\n\n // cut off the triangle\n triangles.push( prev.i / dim );\n triangles.push( ear.i / dim );\n triangles.push( next.i / dim );\n\n removeNode( ear );\n\n // skipping the next vertice leads to less sliver triangles\n ear = next.next;\n stop = next.next;\n\n continue;\n\n }\n\n ear = next;\n\n // if we looped through the whole remaining polygon and can't find any more ears\n\n if ( ear === stop ) {\n\n // try filtering points and slicing again\n\n if ( ! pass ) {\n\n earcutLinked( filterPoints( ear ), triangles, dim, minX, minY, invSize, 1 );\n\n // if this didn't work, try curing all small self-intersections locally\n\n } else if ( pass === 1 ) {\n\n ear = cureLocalIntersections( ear, triangles, dim );\n earcutLinked( ear, triangles, dim, minX, minY, invSize, 2 );\n\n // as a last resort, try splitting the remaining polygon into two\n\n } else if ( pass === 2 ) {\n\n splitEarcut( ear, triangles, dim, minX, minY, invSize );\n\n }\n\n break;\n\n }\n\n }\n\n }\n\n // check whether a polygon node forms a valid ear with adjacent nodes\n\n function isEar( ear ) {\n\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n // now make sure we don't have other points inside the potential ear\n var p = ear.next.next;\n\n while ( p !== ear.prev ) {\n\n if ( pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) && area( p.prev, p, p.next ) >= 0 ) {\n\n return false;\n\n }\n\n p = p.next;\n\n }\n\n return true;\n\n }\n\n function isEarHashed( ear, minX, minY, invSize ) {\n\n var a = ear.prev,\n b = ear,\n c = ear.next;\n\n if ( area( a, b, c ) >= 0 ) return false; // reflex, can't be an ear\n\n // triangle bbox; min & max are calculated like this for speed\n\n var minTX = a.x < b.x ? ( a.x < c.x ? a.x : c.x ) : ( b.x < c.x ? b.x : c.x ),\n minTY = a.y < b.y ? ( a.y < c.y ? a.y : c.y ) : ( b.y < c.y ? b.y : c.y ),\n maxTX = a.x > b.x ? ( a.x > c.x ? a.x : c.x ) : ( b.x > c.x ? b.x : c.x ),\n maxTY = a.y > b.y ? ( a.y > c.y ? a.y : c.y ) : ( b.y > c.y ? b.y : c.y );\n\n // z-order range for the current triangle bbox;\n\n var minZ = zOrder( minTX, minTY, minX, minY, invSize ),\n maxZ = zOrder( maxTX, maxTY, minX, minY, invSize );\n\n // first look for points inside the triangle in increasing z-order\n\n var p = ear.nextZ;\n\n while ( p && p.z <= maxZ ) {\n\n if ( p !== ear.prev && p !== ear.next &&\n pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&\n area( p.prev, p, p.next ) >= 0 ) return false;\n p = p.nextZ;\n\n }\n\n // then look for points in decreasing z-order\n\n p = ear.prevZ;\n\n while ( p && p.z >= minZ ) {\n\n if ( p !== ear.prev && p !== ear.next &&\n pointInTriangle( a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y ) &&\n area( p.prev, p, p.next ) >= 0 ) return false;\n\n p = p.prevZ;\n\n }\n\n return true;\n\n }\n\n // go through all polygon nodes and cure small local self-intersections\n\n function cureLocalIntersections( start, triangles, dim ) {\n\n var p = start;\n\n do {\n\n var a = p.prev, b = p.next.next;\n\n if ( ! equals( a, b ) && intersects( a, p, p.next, b ) && locallyInside( a, b ) && locallyInside( b, a ) ) {\n\n triangles.push( a.i / dim );\n triangles.push( p.i / dim );\n triangles.push( b.i / dim );\n\n // remove two nodes involved\n\n removeNode( p );\n removeNode( p.next );\n\n p = start = b;\n\n }\n\n p = p.next;\n\n } while ( p !== start );\n\n return p;\n\n }\n\n // try splitting polygon into two and triangulate them independently\n\n function splitEarcut( start, triangles, dim, minX, minY, invSize ) {\n\n // look for a valid diagonal that divides the polygon into two\n\n var a = start;\n\n do {\n\n var b = a.next.next;\n\n while ( b !== a.prev ) {\n\n if ( a.i !== b.i && isValidDiagonal( a, b ) ) {\n\n // split the polygon in two by the diagonal\n\n var c = splitPolygon( a, b );\n\n // filter colinear points around the cuts\n\n a = filterPoints( a, a.next );\n c = filterPoints( c, c.next );\n\n // run earcut on each half\n\n earcutLinked( a, triangles, dim, minX, minY, invSize );\n earcutLinked( c, triangles, dim, minX, minY, invSize );\n return;\n\n }\n\n b = b.next;\n\n }\n\n a = a.next;\n\n } while ( a !== start );\n\n }\n\n // link every hole into the outer loop, producing a single-ring polygon without holes\n\n function eliminateHoles( data, holeIndices, outerNode, dim ) {\n\n var queue = [], i, len, start, end, list;\n\n for ( i = 0, len = holeIndices.length; i < len; i ++ ) {\n\n start = holeIndices[ i ] * dim;\n end = i < len - 1 ? holeIndices[ i + 1 ] * dim : data.length;\n list = linkedList( data, start, end, dim, false );\n if ( list === list.next ) list.steiner = true;\n queue.push( getLeftmost( list ) );\n\n }\n\n queue.sort( compareX );\n\n // process holes from left to right\n\n for ( i = 0; i < queue.length; i ++ ) {\n\n eliminateHole( queue[ i ], outerNode );\n outerNode = filterPoints( outerNode, outerNode.next );\n\n }\n\n return outerNode;\n\n }\n\n function compareX( a, b ) {\n\n return a.x - b.x;\n\n }\n\n // find a bridge between vertices that connects hole with an outer ring and and link it\n\n function eliminateHole( hole, outerNode ) {\n\n outerNode = findHoleBridge( hole, outerNode );\n\n if ( outerNode ) {\n\n var b = splitPolygon( outerNode, hole );\n\n filterPoints( b, b.next );\n\n }\n\n }\n\n // David Eberly's algorithm for finding a bridge between hole and outer polygon\n\n function findHoleBridge( hole, outerNode ) {\n\n var p = outerNode,\n hx = hole.x,\n hy = hole.y,\n qx = - Infinity,\n m;\n\n // find a segment intersected by a ray from the hole's leftmost point to the left;\n // segment's endpoint with lesser x will be potential connection point\n\n do {\n\n if ( hy <= p.y && hy >= p.next.y && p.next.y !== p.y ) {\n\n var x = p.x + ( hy - p.y ) * ( p.next.x - p.x ) / ( p.next.y - p.y );\n\n if ( x <= hx && x > qx ) {\n\n qx = x;\n\n if ( x === hx ) {\n\n if ( hy === p.y ) return p;\n if ( hy === p.next.y ) return p.next;\n\n }\n\n m = p.x < p.next.x ? p : p.next;\n\n }\n\n }\n\n p = p.next;\n\n } while ( p !== outerNode );\n\n if ( ! m ) return null;\n\n if ( hx === qx ) return m.prev; // hole touches outer segment; pick lower endpoint\n\n // look for points inside the triangle of hole point, segment intersection and endpoint;\n // if there are no points found, we have a valid connection;\n // otherwise choose the point of the minimum angle with the ray as connection point\n\n var stop = m,\n mx = m.x,\n my = m.y,\n tanMin = Infinity,\n tan;\n\n p = m.next;\n\n while ( p !== stop ) {\n\n if ( hx >= p.x && p.x >= mx && hx !== p.x &&\n pointInTriangle( hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y ) ) {\n\n tan = Math.abs( hy - p.y ) / ( hx - p.x ); // tangential\n\n if ( ( tan < tanMin || ( tan === tanMin && p.x > m.x ) ) && locallyInside( p, hole ) ) {\n\n m = p;\n tanMin = tan;\n\n }\n\n }\n\n p = p.next;\n\n }\n\n return m;\n\n }\n\n // interlink polygon nodes in z-order\n\n function indexCurve( start, minX, minY, invSize ) {\n\n var p = start;\n\n do {\n\n if ( p.z === null ) p.z = zOrder( p.x, p.y, minX, minY, invSize );\n p.prevZ = p.prev;\n p.nextZ = p.next;\n p = p.next;\n\n } while ( p !== start );\n\n p.prevZ.nextZ = null;\n p.prevZ = null;\n\n sortLinked( p );\n\n }\n\n // Simon Tatham's linked list merge sort algorithm\n // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html\n\n function sortLinked( list ) {\n\n var i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;\n\n do {\n\n p = list;\n list = null;\n tail = null;\n numMerges = 0;\n\n while ( p ) {\n\n numMerges ++;\n q = p;\n pSize = 0;\n\n for ( i = 0; i < inSize; i ++ ) {\n\n pSize ++;\n q = q.nextZ;\n if ( ! q ) break;\n\n }\n\n qSize = inSize;\n\n while ( pSize > 0 || ( qSize > 0 && q ) ) {\n\n if ( pSize !== 0 && ( qSize === 0 || ! q || p.z <= q.z ) ) {\n\n e = p;\n p = p.nextZ;\n pSize --;\n\n } else {\n\n e = q;\n q = q.nextZ;\n qSize --;\n\n }\n\n if ( tail ) tail.nextZ = e;\n else list = e;\n\n e.prevZ = tail;\n tail = e;\n\n }\n\n p = q;\n\n }\n\n tail.nextZ = null;\n inSize *= 2;\n\n } while ( numMerges > 1 );\n\n return list;\n\n }\n\n // z-order of a point given coords and inverse of the longer side of data bbox\n\n function zOrder( x, y, minX, minY, invSize ) {\n\n // coords are transformed into non-negative 15-bit integer range\n\n x = 32767 * ( x - minX ) * invSize;\n y = 32767 * ( y - minY ) * invSize;\n\n x = ( x | ( x << 8 ) ) & 0x00FF00FF;\n x = ( x | ( x << 4 ) ) & 0x0F0F0F0F;\n x = ( x | ( x << 2 ) ) & 0x33333333;\n x = ( x | ( x << 1 ) ) & 0x55555555;\n\n y = ( y | ( y << 8 ) ) & 0x00FF00FF;\n y = ( y | ( y << 4 ) ) & 0x0F0F0F0F;\n y = ( y | ( y << 2 ) ) & 0x33333333;\n y = ( y | ( y << 1 ) ) & 0x55555555;\n\n return x | ( y << 1 );\n\n }\n\n // find the leftmost node of a polygon ring\n\n function getLeftmost( start ) {\n\n var p = start, leftmost = start;\n\n do {\n\n if ( p.x < leftmost.x ) leftmost = p;\n p = p.next;\n\n } while ( p !== start );\n\n return leftmost;\n\n }\n\n // check if a point lies within a convex triangle\n\n function pointInTriangle( ax, ay, bx, by, cx, cy, px, py ) {\n\n return ( cx - px ) * ( ay - py ) - ( ax - px ) * ( cy - py ) >= 0 &&\n ( ax - px ) * ( by - py ) - ( bx - px ) * ( ay - py ) >= 0 &&\n ( bx - px ) * ( cy - py ) - ( cx - px ) * ( by - py ) >= 0;\n\n }\n\n // check if a diagonal between two polygon nodes is valid (lies in polygon interior)\n\n function isValidDiagonal( a, b ) {\n\n return a.next.i !== b.i && a.prev.i !== b.i && ! intersectsPolygon( a, b ) &&\n locallyInside( a, b ) && locallyInside( b, a ) && middleInside( a, b );\n\n }\n\n // signed area of a triangle\n\n function area( p, q, r ) {\n\n return ( q.y - p.y ) * ( r.x - q.x ) - ( q.x - p.x ) * ( r.y - q.y );\n\n }\n\n // check if two points are equal\n\n function equals( p1, p2 ) {\n\n return p1.x === p2.x && p1.y === p2.y;\n\n }\n\n // check if two segments intersect\n\n function intersects( p1, q1, p2, q2 ) {\n\n if ( ( equals( p1, q1 ) && equals( p2, q2 ) ) ||\n ( equals( p1, q2 ) && equals( p2, q1 ) ) ) return true;\n\n return area( p1, q1, p2 ) > 0 !== area( p1, q1, q2 ) > 0 &&\n area( p2, q2, p1 ) > 0 !== area( p2, q2, q1 ) > 0;\n\n }\n\n // check if a polygon diagonal intersects any polygon segments\n\n function intersectsPolygon( a, b ) {\n\n var p = a;\n\n do {\n\n if ( p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&\n intersects( p, p.next, a, b ) ) {\n\n return true;\n\n }\n\n p = p.next;\n\n } while ( p !== a );\n\n return false;\n\n }\n\n // check if a polygon diagonal is locally inside the polygon\n\n function locallyInside( a, b ) {\n\n return area( a.prev, a, a.next ) < 0 ?\n area( a, b, a.next ) >= 0 && area( a, a.prev, b ) >= 0 :\n area( a, b, a.prev ) < 0 || area( a, a.next, b ) < 0;\n\n }\n\n // check if the middle point of a polygon diagonal is inside the polygon\n\n function middleInside( a, b ) {\n\n var p = a,\n inside = false,\n px = ( a.x + b.x ) / 2,\n py = ( a.y + b.y ) / 2;\n\n do {\n\n if ( ( ( p.y > py ) !== ( p.next.y > py ) ) && p.next.y !== p.y &&\n ( px < ( p.next.x - p.x ) * ( py - p.y ) / ( p.next.y - p.y ) + p.x ) ) {\n\n inside = ! inside;\n\n }\n\n p = p.next;\n\n } while ( p !== a );\n\n return inside;\n\n }\n\n // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;\n // if one belongs to the outer ring and another to a hole, it merges it into a single ring\n\n function splitPolygon( a, b ) {\n\n var a2 = new Node( a.i, a.x, a.y ),\n b2 = new Node( b.i, b.x, b.y ),\n an = a.next,\n bp = b.prev;\n\n a.next = b;\n b.prev = a;\n\n a2.next = an;\n an.prev = a2;\n\n b2.next = a2;\n a2.prev = b2;\n\n bp.next = b2;\n b2.prev = bp;\n\n return b2;\n\n }\n\n // create a node and optionally link it with previous one (in a circular doubly linked list)\n\n function insertNode( i, x, y, last ) {\n\n var p = new Node( i, x, y );\n\n if ( ! last ) {\n\n p.prev = p;\n p.next = p;\n\n } else {\n\n p.next = last.next;\n p.prev = last;\n last.next.prev = p;\n last.next = p;\n\n }\n\n return p;\n\n }\n\n function removeNode( p ) {\n\n p.next.prev = p.prev;\n p.prev.next = p.next;\n\n if ( p.prevZ ) p.prevZ.nextZ = p.nextZ;\n if ( p.nextZ ) p.nextZ.prevZ = p.prevZ;\n\n }\n\n function Node( i, x, y ) {\n\n // vertice index in coordinates array\n this.i = i;\n\n // vertex coordinates\n this.x = x;\n this.y = y;\n\n // previous and next vertice nodes in a polygon ring\n this.prev = null;\n this.next = null;\n\n // z-order curve value\n this.z = null;\n\n // previous and next nodes in z-order\n this.prevZ = null;\n this.nextZ = null;\n\n // indicates whether this is a steiner point\n this.steiner = false;\n\n }\n\n function signedArea( data, start, end, dim ) {\n\n var sum = 0;\n\n for ( var i = start, j = end - dim; i < end; i += dim ) {\n\n sum += ( data[ j ] - data[ i ] ) * ( data[ i + 1 ] + data[ j + 1 ] );\n j = i;\n\n }\n\n return sum;\n\n }\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n */\n\n var ShapeUtils = {\n\n // calculate area of the contour polygon\n\n area: function ( contour ) {\n\n var n = contour.length;\n var a = 0.0;\n\n for ( var p = n - 1, q = 0; q < n; p = q ++ ) {\n\n a += contour[ p ].x * contour[ q ].y - contour[ q ].x * contour[ p ].y;\n\n }\n\n return a * 0.5;\n\n },\n\n isClockWise: function ( pts ) {\n\n return ShapeUtils.area( pts ) < 0;\n\n },\n\n triangulateShape: function ( contour, holes ) {\n\n var vertices = []; // flat array of vertices like [ x0,y0, x1,y1, x2,y2, ... ]\n var holeIndices = []; // array of hole indices\n var faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]\n\n removeDupEndPts( contour );\n addContour( vertices, contour );\n\n //\n\n var holeIndex = contour.length;\n\n holes.forEach( removeDupEndPts );\n\n for ( var i = 0; i < holes.length; i ++ ) {\n\n holeIndices.push( holeIndex );\n holeIndex += holes[ i ].length;\n addContour( vertices, holes[ i ] );\n\n }\n\n //\n\n var triangles = Earcut.triangulate( vertices, holeIndices );\n\n //\n\n for ( var i = 0; i < triangles.length; i += 3 ) {\n\n faces.push( triangles.slice( i, i + 3 ) );\n\n }\n\n return faces;\n\n }\n\n };\n\n function removeDupEndPts( points ) {\n\n var l = points.length;\n\n if ( l > 2 && points[ l - 1 ].equals( points[ 0 ] ) ) {\n\n points.pop();\n\n }\n\n }\n\n function addContour( vertices, contour ) {\n\n for ( var i = 0; i < contour.length; i ++ ) {\n\n vertices.push( contour[ i ].x );\n vertices.push( contour[ i ].y );\n\n }\n\n }\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n * Creates extruded geometry from a path shape.\n *\n * parameters = {\n *\n * curveSegments: <int>, // number of points on the curves\n * steps: <int>, // number of points for z-side extrusions / used for subdividing segments of extrude spline too\n * amount: <int>, // Depth to extrude the shape\n *\n * bevelEnabled: <bool>, // turn on bevel\n * bevelThickness: <float>, // how deep into the original shape bevel goes\n * bevelSize: <float>, // how far from shape outline is bevel\n * bevelSegments: <int>, // number of bevel layers\n *\n * extrudePath: <THREE.Curve> // curve to extrude shape along\n * frames: <Object> // containing arrays of tangents, normals, binormals\n *\n * UVGenerator: <Object> // object that provides UV generator functions\n *\n * }\n */\n\n // ExtrudeGeometry\n\n function ExtrudeGeometry( shapes, options ) {\n\n Geometry.call( this );\n\n this.type = 'ExtrudeGeometry';\n\n this.parameters = {\n shapes: shapes,\n options: options\n };\n\n this.fromBufferGeometry( new ExtrudeBufferGeometry( shapes, options ) );\n this.mergeVertices();\n\n }\n\n ExtrudeGeometry.prototype = Object.create( Geometry.prototype );\n ExtrudeGeometry.prototype.constructor = ExtrudeGeometry;\n\n // ExtrudeBufferGeometry\n\n function ExtrudeBufferGeometry( shapes, options ) {\n\n if ( typeof ( shapes ) === \"undefined\" ) {\n\n return;\n\n }\n\n BufferGeometry.call( this );\n\n this.type = 'ExtrudeBufferGeometry';\n\n shapes = Array.isArray( shapes ) ? shapes : [ shapes ];\n\n this.addShapeList( shapes, options );\n\n this.computeVertexNormals();\n\n // can't really use automatic vertex normals\n // as then front and back sides get smoothed too\n // should do separate smoothing just for sides\n\n //this.computeVertexNormals();\n\n //console.log( \"took\", ( Date.now() - startTime ) );\n\n }\n\n ExtrudeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ExtrudeBufferGeometry.prototype.constructor = ExtrudeBufferGeometry;\n\n ExtrudeBufferGeometry.prototype.getArrays = function () {\n\n var positionAttribute = this.getAttribute( \"position\" );\n var verticesArray = positionAttribute ? Array.prototype.slice.call( positionAttribute.array ) : [];\n\n var uvAttribute = this.getAttribute( \"uv\" );\n var uvArray = uvAttribute ? Array.prototype.slice.call( uvAttribute.array ) : [];\n\n var IndexAttribute = this.index;\n var indicesArray = IndexAttribute ? Array.prototype.slice.call( IndexAttribute.array ) : [];\n\n return {\n position: verticesArray,\n uv: uvArray,\n index: indicesArray\n };\n\n };\n\n ExtrudeBufferGeometry.prototype.addShapeList = function ( shapes, options ) {\n\n var sl = shapes.length;\n options.arrays = this.getArrays();\n\n for ( var s = 0; s < sl; s ++ ) {\n\n var shape = shapes[ s ];\n this.addShape( shape, options );\n\n }\n\n this.setIndex( options.arrays.index );\n this.addAttribute( 'position', new Float32BufferAttribute( options.arrays.position, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( options.arrays.uv, 2 ) );\n\n };\n\n ExtrudeBufferGeometry.prototype.addShape = function ( shape, options ) {\n\n var arrays = options.arrays ? options.arrays : this.getArrays();\n var verticesArray = arrays.position;\n var indicesArray = arrays.index;\n var uvArray = arrays.uv;\n\n var placeholder = [];\n\n\n var amount = options.amount !== undefined ? options.amount : 100;\n\n var bevelThickness = options.bevelThickness !== undefined ? options.bevelThickness : 6; // 10\n var bevelSize = options.bevelSize !== undefined ? options.bevelSize : bevelThickness - 2; // 8\n var bevelSegments = options.bevelSegments !== undefined ? options.bevelSegments : 3;\n\n var bevelEnabled = options.bevelEnabled !== undefined ? options.bevelEnabled : true; // false\n\n var curveSegments = options.curveSegments !== undefined ? options.curveSegments : 12;\n\n var steps = options.steps !== undefined ? options.steps : 1;\n\n var extrudePath = options.extrudePath;\n var extrudePts, extrudeByPath = false;\n\n // Use default WorldUVGenerator if no UV generators are specified.\n var uvgen = options.UVGenerator !== undefined ? options.UVGenerator : ExtrudeGeometry.WorldUVGenerator;\n\n var splineTube, binormal, normal, position2;\n if ( extrudePath ) {\n\n extrudePts = extrudePath.getSpacedPoints( steps );\n\n extrudeByPath = true;\n bevelEnabled = false; // bevels not supported for path extrusion\n\n // SETUP TNB variables\n\n // TODO1 - have a .isClosed in spline?\n\n splineTube = options.frames !== undefined ? options.frames : extrudePath.computeFrenetFrames( steps, false );\n\n // console.log(splineTube, 'splineTube', splineTube.normals.length, 'steps', steps, 'extrudePts', extrudePts.length);\n\n binormal = new Vector3();\n normal = new Vector3();\n position2 = new Vector3();\n\n }\n\n // Safeguards if bevels are not enabled\n\n if ( ! bevelEnabled ) {\n\n bevelSegments = 0;\n bevelThickness = 0;\n bevelSize = 0;\n\n }\n\n // Variables initialization\n\n var ahole, h, hl; // looping of holes\n var scope = this;\n\n var shapePoints = shape.extractPoints( curveSegments );\n\n var vertices = shapePoints.shape;\n var holes = shapePoints.holes;\n\n var reverse = ! ShapeUtils.isClockWise( vertices );\n\n if ( reverse ) {\n\n vertices = vertices.reverse();\n\n // Maybe we should also check if holes are in the opposite direction, just to be safe ...\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n if ( ShapeUtils.isClockWise( ahole ) ) {\n\n holes[ h ] = ahole.reverse();\n\n }\n\n }\n\n }\n\n\n var faces = ShapeUtils.triangulateShape( vertices, holes );\n\n /* Vertices */\n\n var contour = vertices; // vertices has all points but contour has only points of circumference\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n vertices = vertices.concat( ahole );\n\n }\n\n\n function scalePt2( pt, vec, size ) {\n\n if ( ! vec ) console.error( \"THREE.ExtrudeGeometry: vec does not exist\" );\n\n return vec.clone().multiplyScalar( size ).add( pt );\n\n }\n\n var b, bs, t, z,\n vert, vlen = vertices.length,\n face, flen = faces.length;\n\n\n // Find directions for point movement\n\n\n function getBevelVec( inPt, inPrev, inNext ) {\n\n // computes for inPt the corresponding point inPt' on a new contour\n // shifted by 1 unit (length of normalized vector) to the left\n // if we walk along contour clockwise, this new contour is outside the old one\n //\n // inPt' is the intersection of the two lines parallel to the two\n // adjacent edges of inPt at a distance of 1 unit on the left side.\n\n var v_trans_x, v_trans_y, shrink_by; // resulting translation vector for inPt\n\n // good reading for geometry algorithms (here: line-line intersection)\n // http://geomalgorithms.com/a05-_intersect-1.html\n\n var v_prev_x = inPt.x - inPrev.x,\n v_prev_y = inPt.y - inPrev.y;\n var v_next_x = inNext.x - inPt.x,\n v_next_y = inNext.y - inPt.y;\n\n var v_prev_lensq = ( v_prev_x * v_prev_x + v_prev_y * v_prev_y );\n\n // check for collinear edges\n var collinear0 = ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n if ( Math.abs( collinear0 ) > Number.EPSILON ) {\n\n // not collinear\n\n // length of vectors for normalizing\n\n var v_prev_len = Math.sqrt( v_prev_lensq );\n var v_next_len = Math.sqrt( v_next_x * v_next_x + v_next_y * v_next_y );\n\n // shift adjacent points by unit vectors to the left\n\n var ptPrevShift_x = ( inPrev.x - v_prev_y / v_prev_len );\n var ptPrevShift_y = ( inPrev.y + v_prev_x / v_prev_len );\n\n var ptNextShift_x = ( inNext.x - v_next_y / v_next_len );\n var ptNextShift_y = ( inNext.y + v_next_x / v_next_len );\n\n // scaling factor for v_prev to intersection point\n\n var sf = ( ( ptNextShift_x - ptPrevShift_x ) * v_next_y -\n ( ptNextShift_y - ptPrevShift_y ) * v_next_x ) /\n ( v_prev_x * v_next_y - v_prev_y * v_next_x );\n\n // vector from inPt to intersection point\n\n v_trans_x = ( ptPrevShift_x + v_prev_x * sf - inPt.x );\n v_trans_y = ( ptPrevShift_y + v_prev_y * sf - inPt.y );\n\n // Don't normalize!, otherwise sharp corners become ugly\n // but prevent crazy spikes\n var v_trans_lensq = ( v_trans_x * v_trans_x + v_trans_y * v_trans_y );\n if ( v_trans_lensq <= 2 ) {\n\n return new Vector2( v_trans_x, v_trans_y );\n\n } else {\n\n shrink_by = Math.sqrt( v_trans_lensq / 2 );\n\n }\n\n } else {\n\n // handle special case of collinear edges\n\n var direction_eq = false; // assumes: opposite\n if ( v_prev_x > Number.EPSILON ) {\n\n if ( v_next_x > Number.EPSILON ) {\n\n direction_eq = true;\n\n }\n\n } else {\n\n if ( v_prev_x < - Number.EPSILON ) {\n\n if ( v_next_x < - Number.EPSILON ) {\n\n direction_eq = true;\n\n }\n\n } else {\n\n if ( Math.sign( v_prev_y ) === Math.sign( v_next_y ) ) {\n\n direction_eq = true;\n\n }\n\n }\n\n }\n\n if ( direction_eq ) {\n\n // console.log(\"Warning: lines are a straight sequence\");\n v_trans_x = - v_prev_y;\n v_trans_y = v_prev_x;\n shrink_by = Math.sqrt( v_prev_lensq );\n\n } else {\n\n // console.log(\"Warning: lines are a straight spike\");\n v_trans_x = v_prev_x;\n v_trans_y = v_prev_y;\n shrink_by = Math.sqrt( v_prev_lensq / 2 );\n\n }\n\n }\n\n return new Vector2( v_trans_x / shrink_by, v_trans_y / shrink_by );\n\n }\n\n\n var contourMovements = [];\n\n for ( var i = 0, il = contour.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n if ( j === il ) j = 0;\n if ( k === il ) k = 0;\n\n // (j)---(i)---(k)\n // console.log('i,j,k', i, j , k)\n\n contourMovements[ i ] = getBevelVec( contour[ i ], contour[ j ], contour[ k ] );\n\n }\n\n var holesMovements = [],\n oneHoleMovements, verticesMovements = contourMovements.concat();\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n\n oneHoleMovements = [];\n\n for ( i = 0, il = ahole.length, j = il - 1, k = i + 1; i < il; i ++, j ++, k ++ ) {\n\n if ( j === il ) j = 0;\n if ( k === il ) k = 0;\n\n // (j)---(i)---(k)\n oneHoleMovements[ i ] = getBevelVec( ahole[ i ], ahole[ j ], ahole[ k ] );\n\n }\n\n holesMovements.push( oneHoleMovements );\n verticesMovements = verticesMovements.concat( oneHoleMovements );\n\n }\n\n\n // Loop bevelSegments, 1 for the front, 1 for the back\n\n for ( b = 0; b < bevelSegments; b ++ ) {\n\n //for ( b = bevelSegments; b > 0; b -- ) {\n\n t = b / bevelSegments;\n z = bevelThickness * Math.cos( t * Math.PI / 2 );\n bs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n // contract shape\n\n for ( i = 0, il = contour.length; i < il; i ++ ) {\n\n vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n\n v( vert.x, vert.y, - z );\n\n }\n\n // expand holes\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n oneHoleMovements = holesMovements[ h ];\n\n for ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n v( vert.x, vert.y, - z );\n\n }\n\n }\n\n }\n\n bs = bevelSize;\n\n // Back facing vertices\n\n for ( i = 0; i < vlen; i ++ ) {\n\n vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, 0 );\n\n } else {\n\n // v( vert.x, vert.y + extrudePts[ 0 ].y, extrudePts[ 0 ].x );\n\n normal.copy( splineTube.normals[ 0 ] ).multiplyScalar( vert.x );\n binormal.copy( splineTube.binormals[ 0 ] ).multiplyScalar( vert.y );\n\n position2.copy( extrudePts[ 0 ] ).add( normal ).add( binormal );\n\n v( position2.x, position2.y, position2.z );\n\n }\n\n }\n\n // Add stepped vertices...\n // Including front facing vertices\n\n var s;\n\n for ( s = 1; s <= steps; s ++ ) {\n\n for ( i = 0; i < vlen; i ++ ) {\n\n vert = bevelEnabled ? scalePt2( vertices[ i ], verticesMovements[ i ], bs ) : vertices[ i ];\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, amount / steps * s );\n\n } else {\n\n // v( vert.x, vert.y + extrudePts[ s - 1 ].y, extrudePts[ s - 1 ].x );\n\n normal.copy( splineTube.normals[ s ] ).multiplyScalar( vert.x );\n binormal.copy( splineTube.binormals[ s ] ).multiplyScalar( vert.y );\n\n position2.copy( extrudePts[ s ] ).add( normal ).add( binormal );\n\n v( position2.x, position2.y, position2.z );\n\n }\n\n }\n\n }\n\n\n // Add bevel segments planes\n\n //for ( b = 1; b <= bevelSegments; b ++ ) {\n for ( b = bevelSegments - 1; b >= 0; b -- ) {\n\n t = b / bevelSegments;\n z = bevelThickness * Math.cos( t * Math.PI / 2 );\n bs = bevelSize * Math.sin( t * Math.PI / 2 );\n\n // contract shape\n\n for ( i = 0, il = contour.length; i < il; i ++ ) {\n\n vert = scalePt2( contour[ i ], contourMovements[ i ], bs );\n v( vert.x, vert.y, amount + z );\n\n }\n\n // expand holes\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n oneHoleMovements = holesMovements[ h ];\n\n for ( i = 0, il = ahole.length; i < il; i ++ ) {\n\n vert = scalePt2( ahole[ i ], oneHoleMovements[ i ], bs );\n\n if ( ! extrudeByPath ) {\n\n v( vert.x, vert.y, amount + z );\n\n } else {\n\n v( vert.x, vert.y + extrudePts[ steps - 1 ].y, extrudePts[ steps - 1 ].x + z );\n\n }\n\n }\n\n }\n\n }\n\n /* Faces */\n\n // Top and bottom faces\n\n buildLidFaces();\n\n // Sides faces\n\n buildSideFaces();\n\n\n ///// Internal functions\n\n function buildLidFaces() {\n\n var start = verticesArray.length / 3;\n\n if ( bevelEnabled ) {\n\n var layer = 0; // steps + 1\n var offset = vlen * layer;\n\n // Bottom faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 2 ] + offset, face[ 1 ] + offset, face[ 0 ] + offset );\n\n }\n\n layer = steps + bevelSegments * 2;\n offset = vlen * layer;\n\n // Top faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 0 ] + offset, face[ 1 ] + offset, face[ 2 ] + offset );\n\n }\n\n } else {\n\n // Bottom faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 2 ], face[ 1 ], face[ 0 ] );\n\n }\n\n // Top faces\n\n for ( i = 0; i < flen; i ++ ) {\n\n face = faces[ i ];\n f3( face[ 0 ] + vlen * steps, face[ 1 ] + vlen * steps, face[ 2 ] + vlen * steps );\n\n }\n\n }\n\n scope.addGroup( start, verticesArray.length / 3 - start, 0 );\n\n }\n\n // Create faces for the z-sides of the shape\n\n function buildSideFaces() {\n\n var start = verticesArray.length / 3;\n var layeroffset = 0;\n sidewalls( contour, layeroffset );\n layeroffset += contour.length;\n\n for ( h = 0, hl = holes.length; h < hl; h ++ ) {\n\n ahole = holes[ h ];\n sidewalls( ahole, layeroffset );\n\n //, true\n layeroffset += ahole.length;\n\n }\n\n\n scope.addGroup( start, verticesArray.length / 3 - start, 1 );\n\n\n }\n\n function sidewalls( contour, layeroffset ) {\n\n var j, k;\n i = contour.length;\n\n while ( -- i >= 0 ) {\n\n j = i;\n k = i - 1;\n if ( k < 0 ) k = contour.length - 1;\n\n //console.log('b', i,j, i-1, k,vertices.length);\n\n var s = 0,\n sl = steps + bevelSegments * 2;\n\n for ( s = 0; s < sl; s ++ ) {\n\n var slen1 = vlen * s;\n var slen2 = vlen * ( s + 1 );\n\n var a = layeroffset + j + slen1,\n b = layeroffset + k + slen1,\n c = layeroffset + k + slen2,\n d = layeroffset + j + slen2;\n\n f4( a, b, c, d );\n\n }\n\n }\n\n }\n\n function v( x, y, z ) {\n\n placeholder.push( x );\n placeholder.push( y );\n placeholder.push( z );\n\n }\n\n\n function f3( a, b, c ) {\n\n addVertex( a );\n addVertex( b );\n addVertex( c );\n\n var nextIndex = verticesArray.length / 3;\n var uvs = uvgen.generateTopUV( scope, verticesArray, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n addUV( uvs[ 0 ] );\n addUV( uvs[ 1 ] );\n addUV( uvs[ 2 ] );\n\n }\n\n function f4( a, b, c, d ) {\n\n addVertex( a );\n addVertex( b );\n addVertex( d );\n\n addVertex( b );\n addVertex( c );\n addVertex( d );\n\n\n var nextIndex = verticesArray.length / 3;\n var uvs = uvgen.generateSideWallUV( scope, verticesArray, nextIndex - 6, nextIndex - 3, nextIndex - 2, nextIndex - 1 );\n\n addUV( uvs[ 0 ] );\n addUV( uvs[ 1 ] );\n addUV( uvs[ 3 ] );\n\n addUV( uvs[ 1 ] );\n addUV( uvs[ 2 ] );\n addUV( uvs[ 3 ] );\n\n }\n\n function addVertex( index ) {\n\n indicesArray.push( verticesArray.length / 3 );\n verticesArray.push( placeholder[ index * 3 + 0 ] );\n verticesArray.push( placeholder[ index * 3 + 1 ] );\n verticesArray.push( placeholder[ index * 3 + 2 ] );\n\n }\n\n\n function addUV( vector2 ) {\n\n uvArray.push( vector2.x );\n uvArray.push( vector2.y );\n\n }\n\n if ( ! options.arrays ) {\n\n this.setIndex( indicesArray );\n this.addAttribute( 'position', new Float32BufferAttribute( verticesArray, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvArray, 2 ) );\n\n }\n\n };\n\n ExtrudeGeometry.WorldUVGenerator = {\n\n generateTopUV: function ( geometry, vertices, indexA, indexB, indexC ) {\n\n var a_x = vertices[ indexA * 3 ];\n var a_y = vertices[ indexA * 3 + 1 ];\n var b_x = vertices[ indexB * 3 ];\n var b_y = vertices[ indexB * 3 + 1 ];\n var c_x = vertices[ indexC * 3 ];\n var c_y = vertices[ indexC * 3 + 1 ];\n\n return [\n new Vector2( a_x, a_y ),\n new Vector2( b_x, b_y ),\n new Vector2( c_x, c_y )\n ];\n\n },\n\n generateSideWallUV: function ( geometry, vertices, indexA, indexB, indexC, indexD ) {\n\n var a_x = vertices[ indexA * 3 ];\n var a_y = vertices[ indexA * 3 + 1 ];\n var a_z = vertices[ indexA * 3 + 2 ];\n var b_x = vertices[ indexB * 3 ];\n var b_y = vertices[ indexB * 3 + 1 ];\n var b_z = vertices[ indexB * 3 + 2 ];\n var c_x = vertices[ indexC * 3 ];\n var c_y = vertices[ indexC * 3 + 1 ];\n var c_z = vertices[ indexC * 3 + 2 ];\n var d_x = vertices[ indexD * 3 ];\n var d_y = vertices[ indexD * 3 + 1 ];\n var d_z = vertices[ indexD * 3 + 2 ];\n\n if ( Math.abs( a_y - b_y ) < 0.01 ) {\n\n return [\n new Vector2( a_x, 1 - a_z ),\n new Vector2( b_x, 1 - b_z ),\n new Vector2( c_x, 1 - c_z ),\n new Vector2( d_x, 1 - d_z )\n ];\n\n } else {\n\n return [\n new Vector2( a_y, 1 - a_z ),\n new Vector2( b_y, 1 - b_z ),\n new Vector2( c_y, 1 - c_z ),\n new Vector2( d_y, 1 - d_z )\n ];\n\n }\n\n }\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author alteredq / http://alteredqualia.com/\n *\n * Text = 3D Text\n *\n * parameters = {\n * font: <THREE.Font>, // font\n *\n * size: <float>, // size of the text\n * height: <float>, // thickness to extrude text\n * curveSegments: <int>, // number of points on the curves\n *\n * bevelEnabled: <bool>, // turn on bevel\n * bevelThickness: <float>, // how deep into text bevel goes\n * bevelSize: <float> // how far from text outline is bevel\n * }\n */\n\n // TextGeometry\n\n function TextGeometry( text, parameters ) {\n\n Geometry.call( this );\n\n this.type = 'TextGeometry';\n\n this.parameters = {\n text: text,\n parameters: parameters\n };\n\n this.fromBufferGeometry( new TextBufferGeometry( text, parameters ) );\n this.mergeVertices();\n\n }\n\n TextGeometry.prototype = Object.create( Geometry.prototype );\n TextGeometry.prototype.constructor = TextGeometry;\n\n // TextBufferGeometry\n\n function TextBufferGeometry( text, parameters ) {\n\n parameters = parameters || {};\n\n var font = parameters.font;\n\n if ( ! ( font && font.isFont ) ) {\n\n console.error( 'THREE.TextGeometry: font parameter is not an instance of THREE.Font.' );\n return new Geometry();\n\n }\n\n var shapes = font.generateShapes( text, parameters.size, parameters.curveSegments );\n\n // translate parameters to ExtrudeGeometry API\n\n parameters.amount = parameters.height !== undefined ? parameters.height : 50;\n\n // defaults\n\n if ( parameters.bevelThickness === undefined ) parameters.bevelThickness = 10;\n if ( parameters.bevelSize === undefined ) parameters.bevelSize = 8;\n if ( parameters.bevelEnabled === undefined ) parameters.bevelEnabled = false;\n\n ExtrudeBufferGeometry.call( this, shapes, parameters );\n\n this.type = 'TextBufferGeometry';\n\n }\n\n TextBufferGeometry.prototype = Object.create( ExtrudeBufferGeometry.prototype );\n TextBufferGeometry.prototype.constructor = TextBufferGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author benaadams / https://twitter.com/ben_a_adams\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // SphereGeometry\n\n function SphereGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'SphereGeometry';\n\n this.parameters = {\n radius: radius,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n phiStart: phiStart,\n phiLength: phiLength,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n SphereGeometry.prototype = Object.create( Geometry.prototype );\n SphereGeometry.prototype.constructor = SphereGeometry;\n\n // SphereBufferGeometry\n\n function SphereBufferGeometry( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'SphereBufferGeometry';\n\n this.parameters = {\n radius: radius,\n widthSegments: widthSegments,\n heightSegments: heightSegments,\n phiStart: phiStart,\n phiLength: phiLength,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n radius = radius || 1;\n\n widthSegments = Math.max( 3, Math.floor( widthSegments ) || 8 );\n heightSegments = Math.max( 2, Math.floor( heightSegments ) || 6 );\n\n phiStart = phiStart !== undefined ? phiStart : 0;\n phiLength = phiLength !== undefined ? phiLength : Math.PI * 2;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI;\n\n var thetaEnd = thetaStart + thetaLength;\n\n var ix, iy;\n\n var index = 0;\n var grid = [];\n\n var vertex = new Vector3();\n var normal = new Vector3();\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // generate vertices, normals and uvs\n\n for ( iy = 0; iy <= heightSegments; iy ++ ) {\n\n var verticesRow = [];\n\n var v = iy / heightSegments;\n\n for ( ix = 0; ix <= widthSegments; ix ++ ) {\n\n var u = ix / widthSegments;\n\n // vertex\n\n vertex.x = - radius * Math.cos( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n vertex.y = radius * Math.cos( thetaStart + v * thetaLength );\n vertex.z = radius * Math.sin( phiStart + u * phiLength ) * Math.sin( thetaStart + v * thetaLength );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normal.set( vertex.x, vertex.y, vertex.z ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, 1 - v );\n\n verticesRow.push( index ++ );\n\n }\n\n grid.push( verticesRow );\n\n }\n\n // indices\n\n for ( iy = 0; iy < heightSegments; iy ++ ) {\n\n for ( ix = 0; ix < widthSegments; ix ++ ) {\n\n var a = grid[ iy ][ ix + 1 ];\n var b = grid[ iy ][ ix ];\n var c = grid[ iy + 1 ][ ix ];\n var d = grid[ iy + 1 ][ ix + 1 ];\n\n if ( iy !== 0 || thetaStart > 0 ) indices.push( a, b, d );\n if ( iy !== heightSegments - 1 || thetaEnd < Math.PI ) indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n SphereBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n SphereBufferGeometry.prototype.constructor = SphereBufferGeometry;\n\n /**\n * @author Kaleb Murphy\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // RingGeometry\n\n function RingGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'RingGeometry';\n\n this.parameters = {\n innerRadius: innerRadius,\n outerRadius: outerRadius,\n thetaSegments: thetaSegments,\n phiSegments: phiSegments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n RingGeometry.prototype = Object.create( Geometry.prototype );\n RingGeometry.prototype.constructor = RingGeometry;\n\n // RingBufferGeometry\n\n function RingBufferGeometry( innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'RingBufferGeometry';\n\n this.parameters = {\n innerRadius: innerRadius,\n outerRadius: outerRadius,\n thetaSegments: thetaSegments,\n phiSegments: phiSegments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n innerRadius = innerRadius || 0.5;\n outerRadius = outerRadius || 1;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n thetaSegments = thetaSegments !== undefined ? Math.max( 3, thetaSegments ) : 8;\n phiSegments = phiSegments !== undefined ? Math.max( 1, phiSegments ) : 1;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // some helper variables\n\n var segment;\n var radius = innerRadius;\n var radiusStep = ( ( outerRadius - innerRadius ) / phiSegments );\n var vertex = new Vector3();\n var uv = new Vector2();\n var j, i;\n\n // generate vertices, normals and uvs\n\n for ( j = 0; j <= phiSegments; j ++ ) {\n\n for ( i = 0; i <= thetaSegments; i ++ ) {\n\n // values are generate from the inside of the ring to the outside\n\n segment = thetaStart + i / thetaSegments * thetaLength;\n\n // vertex\n\n vertex.x = radius * Math.cos( segment );\n vertex.y = radius * Math.sin( segment );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, 0, 1 );\n\n // uv\n\n uv.x = ( vertex.x / outerRadius + 1 ) / 2;\n uv.y = ( vertex.y / outerRadius + 1 ) / 2;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n // increase the radius for next row of vertices\n\n radius += radiusStep;\n\n }\n\n // indices\n\n for ( j = 0; j < phiSegments; j ++ ) {\n\n var thetaSegmentLevel = j * ( thetaSegments + 1 );\n\n for ( i = 0; i < thetaSegments; i ++ ) {\n\n segment = i + thetaSegmentLevel;\n\n var a = segment;\n var b = segment + thetaSegments + 1;\n var c = segment + thetaSegments + 2;\n var d = segment + 1;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n RingBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n RingBufferGeometry.prototype.constructor = RingBufferGeometry;\n\n /**\n * @author astrodud / http://astrodud.isgreat.org/\n * @author zz85 / https://github.com/zz85\n * @author bhouston / http://clara.io\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // LatheGeometry\n\n function LatheGeometry( points, segments, phiStart, phiLength ) {\n\n Geometry.call( this );\n\n this.type = 'LatheGeometry';\n\n this.parameters = {\n points: points,\n segments: segments,\n phiStart: phiStart,\n phiLength: phiLength\n };\n\n this.fromBufferGeometry( new LatheBufferGeometry( points, segments, phiStart, phiLength ) );\n this.mergeVertices();\n\n }\n\n LatheGeometry.prototype = Object.create( Geometry.prototype );\n LatheGeometry.prototype.constructor = LatheGeometry;\n\n // LatheBufferGeometry\n\n function LatheBufferGeometry( points, segments, phiStart, phiLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'LatheBufferGeometry';\n\n this.parameters = {\n points: points,\n segments: segments,\n phiStart: phiStart,\n phiLength: phiLength\n };\n\n segments = Math.floor( segments ) || 12;\n phiStart = phiStart || 0;\n phiLength = phiLength || Math.PI * 2;\n\n // clamp phiLength so it's in range of [ 0, 2PI ]\n\n phiLength = _Math.clamp( phiLength, 0, Math.PI * 2 );\n\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var uvs = [];\n\n // helper variables\n\n var base;\n var inverseSegments = 1.0 / segments;\n var vertex = new Vector3();\n var uv = new Vector2();\n var i, j;\n\n // generate vertices and uvs\n\n for ( i = 0; i <= segments; i ++ ) {\n\n var phi = phiStart + i * inverseSegments * phiLength;\n\n var sin = Math.sin( phi );\n var cos = Math.cos( phi );\n\n for ( j = 0; j <= ( points.length - 1 ); j ++ ) {\n\n // vertex\n\n vertex.x = points[ j ].x * sin;\n vertex.y = points[ j ].y;\n vertex.z = points[ j ].x * cos;\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // uv\n\n uv.x = i / segments;\n uv.y = j / ( points.length - 1 );\n\n uvs.push( uv.x, uv.y );\n\n\n }\n\n }\n\n // indices\n\n for ( i = 0; i < segments; i ++ ) {\n\n for ( j = 0; j < ( points.length - 1 ); j ++ ) {\n\n base = j + i * points.length;\n\n var a = base;\n var b = base + points.length;\n var c = base + points.length + 1;\n var d = base + 1;\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n // generate normals\n\n this.computeVertexNormals();\n\n // if the geometry is closed, we need to average the normals along the seam.\n // because the corresponding vertices are identical (but still have different UVs).\n\n if ( phiLength === Math.PI * 2 ) {\n\n var normals = this.attributes.normal.array;\n var n1 = new Vector3();\n var n2 = new Vector3();\n var n = new Vector3();\n\n // this is the buffer offset for the last line of vertices\n\n base = segments * points.length * 3;\n\n for ( i = 0, j = 0; i < points.length; i ++, j += 3 ) {\n\n // select the normal of the vertex in the first line\n\n n1.x = normals[ j + 0 ];\n n1.y = normals[ j + 1 ];\n n1.z = normals[ j + 2 ];\n\n // select the normal of the vertex in the last line\n\n n2.x = normals[ base + j + 0 ];\n n2.y = normals[ base + j + 1 ];\n n2.z = normals[ base + j + 2 ];\n\n // average normals\n\n n.addVectors( n1, n2 ).normalize();\n\n // assign the new values to both normals\n\n normals[ j + 0 ] = normals[ base + j + 0 ] = n.x;\n normals[ j + 1 ] = normals[ base + j + 1 ] = n.y;\n normals[ j + 2 ] = normals[ base + j + 2 ] = n.z;\n\n }\n\n }\n\n }\n\n LatheBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n LatheBufferGeometry.prototype.constructor = LatheBufferGeometry;\n\n /**\n * @author jonobr1 / http://jonobr1.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // ShapeGeometry\n\n function ShapeGeometry( shapes, curveSegments ) {\n\n Geometry.call( this );\n\n this.type = 'ShapeGeometry';\n\n if ( typeof curveSegments === 'object' ) {\n\n console.warn( 'THREE.ShapeGeometry: Options parameter has been removed.' );\n\n curveSegments = curveSegments.curveSegments;\n\n }\n\n this.parameters = {\n shapes: shapes,\n curveSegments: curveSegments\n };\n\n this.fromBufferGeometry( new ShapeBufferGeometry( shapes, curveSegments ) );\n this.mergeVertices();\n\n }\n\n ShapeGeometry.prototype = Object.create( Geometry.prototype );\n ShapeGeometry.prototype.constructor = ShapeGeometry;\n\n ShapeGeometry.prototype.toJSON = function () {\n\n var data = Geometry.prototype.toJSON.call( this );\n\n var shapes = this.parameters.shapes;\n\n return toJSON( shapes, data );\n\n };\n\n // ShapeBufferGeometry\n\n function ShapeBufferGeometry( shapes, curveSegments ) {\n\n BufferGeometry.call( this );\n\n this.type = 'ShapeBufferGeometry';\n\n this.parameters = {\n shapes: shapes,\n curveSegments: curveSegments\n };\n\n curveSegments = curveSegments || 12;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var groupStart = 0;\n var groupCount = 0;\n\n // allow single and array values for \"shapes\" parameter\n\n if ( Array.isArray( shapes ) === false ) {\n\n addShape( shapes );\n\n } else {\n\n for ( var i = 0; i < shapes.length; i ++ ) {\n\n addShape( shapes[ i ] );\n\n this.addGroup( groupStart, groupCount, i ); // enables MultiMaterial support\n\n groupStart += groupCount;\n groupCount = 0;\n\n }\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n\n // helper functions\n\n function addShape( shape ) {\n\n var i, l, shapeHole;\n\n var indexOffset = vertices.length / 3;\n var points = shape.extractPoints( curveSegments );\n\n var shapeVertices = points.shape;\n var shapeHoles = points.holes;\n\n // check direction of vertices\n\n if ( ShapeUtils.isClockWise( shapeVertices ) === false ) {\n\n shapeVertices = shapeVertices.reverse();\n\n // also check if holes are in the opposite direction\n\n for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n shapeHole = shapeHoles[ i ];\n\n if ( ShapeUtils.isClockWise( shapeHole ) === true ) {\n\n shapeHoles[ i ] = shapeHole.reverse();\n\n }\n\n }\n\n }\n\n var faces = ShapeUtils.triangulateShape( shapeVertices, shapeHoles );\n\n // join vertices of inner and outer paths to a single array\n\n for ( i = 0, l = shapeHoles.length; i < l; i ++ ) {\n\n shapeHole = shapeHoles[ i ];\n shapeVertices = shapeVertices.concat( shapeHole );\n\n }\n\n // vertices, normals, uvs\n\n for ( i = 0, l = shapeVertices.length; i < l; i ++ ) {\n\n var vertex = shapeVertices[ i ];\n\n vertices.push( vertex.x, vertex.y, 0 );\n normals.push( 0, 0, 1 );\n uvs.push( vertex.x, vertex.y ); // world uvs\n\n }\n\n // incides\n\n for ( i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n var a = face[ 0 ] + indexOffset;\n var b = face[ 1 ] + indexOffset;\n var c = face[ 2 ] + indexOffset;\n\n indices.push( a, b, c );\n groupCount += 3;\n\n }\n\n }\n\n }\n\n ShapeBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n ShapeBufferGeometry.prototype.constructor = ShapeBufferGeometry;\n\n ShapeBufferGeometry.prototype.toJSON = function () {\n\n var data = BufferGeometry.prototype.toJSON.call( this );\n\n var shapes = this.parameters.shapes;\n\n return toJSON( shapes, data );\n\n };\n\n //\n\n function toJSON( shapes, data ) {\n\n data.shapes = [];\n\n if ( Array.isArray( shapes ) ) {\n\n for ( var i = 0, l = shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ i ];\n\n data.shapes.push( shape.uuid );\n\n }\n\n } else {\n\n data.shapes.push( shapes.uuid );\n\n }\n\n return data;\n\n }\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function EdgesGeometry( geometry, thresholdAngle ) {\n\n BufferGeometry.call( this );\n\n this.type = 'EdgesGeometry';\n\n this.parameters = {\n thresholdAngle: thresholdAngle\n };\n\n thresholdAngle = ( thresholdAngle !== undefined ) ? thresholdAngle : 1;\n\n // buffer\n\n var vertices = [];\n\n // helper variables\n\n var thresholdDot = Math.cos( _Math.DEG2RAD * thresholdAngle );\n var edge = [ 0, 0 ], edges = {}, edge1, edge2;\n var key, keys = [ 'a', 'b', 'c' ];\n\n // prepare source geometry\n\n var geometry2;\n\n if ( geometry.isBufferGeometry ) {\n\n geometry2 = new Geometry();\n geometry2.fromBufferGeometry( geometry );\n\n } else {\n\n geometry2 = geometry.clone();\n\n }\n\n geometry2.mergeVertices();\n geometry2.computeFaceNormals();\n\n var sourceVertices = geometry2.vertices;\n var faces = geometry2.faces;\n\n // now create a data structure where each entry represents an edge with its adjoining faces\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( var j = 0; j < 3; j ++ ) {\n\n edge1 = face[ keys[ j ] ];\n edge2 = face[ keys[ ( j + 1 ) % 3 ] ];\n edge[ 0 ] = Math.min( edge1, edge2 );\n edge[ 1 ] = Math.max( edge1, edge2 );\n\n key = edge[ 0 ] + ',' + edge[ 1 ];\n\n if ( edges[ key ] === undefined ) {\n\n edges[ key ] = { index1: edge[ 0 ], index2: edge[ 1 ], face1: i, face2: undefined };\n\n } else {\n\n edges[ key ].face2 = i;\n\n }\n\n }\n\n }\n\n // generate vertices\n\n for ( key in edges ) {\n\n var e = edges[ key ];\n\n // an edge is only rendered if the angle (in degrees) between the face normals of the adjoining faces exceeds this value. default = 1 degree.\n\n if ( e.face2 === undefined || faces[ e.face1 ].normal.dot( faces[ e.face2 ].normal ) <= thresholdDot ) {\n\n var vertex = sourceVertices[ e.index1 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n vertex = sourceVertices[ e.index2 ];\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n }\n\n }\n\n // build geometry\n\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n\n }\n\n EdgesGeometry.prototype = Object.create( BufferGeometry.prototype );\n EdgesGeometry.prototype.constructor = EdgesGeometry;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n // CylinderGeometry\n\n function CylinderGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'CylinderGeometry';\n\n this.parameters = {\n radiusTop: radiusTop,\n radiusBottom: radiusBottom,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n CylinderGeometry.prototype = Object.create( Geometry.prototype );\n CylinderGeometry.prototype.constructor = CylinderGeometry;\n\n // CylinderBufferGeometry\n\n function CylinderBufferGeometry( radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'CylinderBufferGeometry';\n\n this.parameters = {\n radiusTop: radiusTop,\n radiusBottom: radiusBottom,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n var scope = this;\n\n radiusTop = radiusTop !== undefined ? radiusTop : 1;\n radiusBottom = radiusBottom !== undefined ? radiusBottom : 1;\n height = height || 1;\n\n radialSegments = Math.floor( radialSegments ) || 8;\n heightSegments = Math.floor( heightSegments ) || 1;\n\n openEnded = openEnded !== undefined ? openEnded : false;\n thetaStart = thetaStart !== undefined ? thetaStart : 0.0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var index = 0;\n var indexArray = [];\n var halfHeight = height / 2;\n var groupStart = 0;\n\n // generate geometry\n\n generateTorso();\n\n if ( openEnded === false ) {\n\n if ( radiusTop > 0 ) generateCap( true );\n if ( radiusBottom > 0 ) generateCap( false );\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n function generateTorso() {\n\n var x, y;\n var normal = new Vector3();\n var vertex = new Vector3();\n\n var groupCount = 0;\n\n // this will be used to calculate the normal\n var slope = ( radiusBottom - radiusTop ) / height;\n\n // generate vertices, normals and uvs\n\n for ( y = 0; y <= heightSegments; y ++ ) {\n\n var indexRow = [];\n\n var v = y / heightSegments;\n\n // calculate the radius of the current row\n\n var radius = v * ( radiusBottom - radiusTop ) + radiusTop;\n\n for ( x = 0; x <= radialSegments; x ++ ) {\n\n var u = x / radialSegments;\n\n var theta = u * thetaLength + thetaStart;\n\n var sinTheta = Math.sin( theta );\n var cosTheta = Math.cos( theta );\n\n // vertex\n\n vertex.x = radius * sinTheta;\n vertex.y = - v * height + halfHeight;\n vertex.z = radius * cosTheta;\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normal.set( sinTheta, slope, cosTheta ).normalize();\n normals.push( normal.x, normal.y, normal.z );\n\n // uv\n\n uvs.push( u, 1 - v );\n\n // save index of vertex in respective row\n\n indexRow.push( index ++ );\n\n }\n\n // now save vertices of the row in our index array\n\n indexArray.push( indexRow );\n\n }\n\n // generate indices\n\n for ( x = 0; x < radialSegments; x ++ ) {\n\n for ( y = 0; y < heightSegments; y ++ ) {\n\n // we use the index array to access the correct indices\n\n var a = indexArray[ y ][ x ];\n var b = indexArray[ y + 1 ][ x ];\n var c = indexArray[ y + 1 ][ x + 1 ];\n var d = indexArray[ y ][ x + 1 ];\n\n // faces\n\n indices.push( a, b, d );\n indices.push( b, c, d );\n\n // update group counter\n\n groupCount += 6;\n\n }\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, 0 );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n }\n\n function generateCap( top ) {\n\n var x, centerIndexStart, centerIndexEnd;\n\n var uv = new Vector2();\n var vertex = new Vector3();\n\n var groupCount = 0;\n\n var radius = ( top === true ) ? radiusTop : radiusBottom;\n var sign = ( top === true ) ? 1 : - 1;\n\n // save the index of the first center vertex\n centerIndexStart = index;\n\n // first we generate the center vertex data of the cap.\n // because the geometry needs one set of uvs per face,\n // we must generate a center vertex per face/segment\n\n for ( x = 1; x <= radialSegments; x ++ ) {\n\n // vertex\n\n vertices.push( 0, halfHeight * sign, 0 );\n\n // normal\n\n normals.push( 0, sign, 0 );\n\n // uv\n\n uvs.push( 0.5, 0.5 );\n\n // increase index\n\n index ++;\n\n }\n\n // save the index of the last center vertex\n\n centerIndexEnd = index;\n\n // now we generate the surrounding vertices, normals and uvs\n\n for ( x = 0; x <= radialSegments; x ++ ) {\n\n var u = x / radialSegments;\n var theta = u * thetaLength + thetaStart;\n\n var cosTheta = Math.cos( theta );\n var sinTheta = Math.sin( theta );\n\n // vertex\n\n vertex.x = radius * sinTheta;\n vertex.y = halfHeight * sign;\n vertex.z = radius * cosTheta;\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, sign, 0 );\n\n // uv\n\n uv.x = ( cosTheta * 0.5 ) + 0.5;\n uv.y = ( sinTheta * 0.5 * sign ) + 0.5;\n uvs.push( uv.x, uv.y );\n\n // increase index\n\n index ++;\n\n }\n\n // generate indices\n\n for ( x = 0; x < radialSegments; x ++ ) {\n\n var c = centerIndexStart + x;\n var i = centerIndexEnd + x;\n\n if ( top === true ) {\n\n // face top\n\n indices.push( i, i + 1, c );\n\n } else {\n\n // face bottom\n\n indices.push( i + 1, i, c );\n\n }\n\n groupCount += 3;\n\n }\n\n // add a group to the geometry. this will ensure multi material support\n\n scope.addGroup( groupStart, groupCount, top === true ? 1 : 2 );\n\n // calculate new start value for groups\n\n groupStart += groupCount;\n\n }\n\n }\n\n CylinderBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n CylinderBufferGeometry.prototype.constructor = CylinderBufferGeometry;\n\n /**\n * @author abelnation / http://github.com/abelnation\n */\n\n // ConeGeometry\n\n function ConeGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n CylinderGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n this.type = 'ConeGeometry';\n\n this.parameters = {\n radius: radius,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n }\n\n ConeGeometry.prototype = Object.create( CylinderGeometry.prototype );\n ConeGeometry.prototype.constructor = ConeGeometry;\n\n // ConeBufferGeometry\n\n function ConeBufferGeometry( radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength ) {\n\n CylinderBufferGeometry.call( this, 0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength );\n\n this.type = 'ConeBufferGeometry';\n\n this.parameters = {\n radius: radius,\n height: height,\n radialSegments: radialSegments,\n heightSegments: heightSegments,\n openEnded: openEnded,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n }\n\n ConeBufferGeometry.prototype = Object.create( CylinderBufferGeometry.prototype );\n ConeBufferGeometry.prototype.constructor = ConeBufferGeometry;\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n * @author Mugen87 / https://github.com/Mugen87\n * @author hughes\n */\n\n // CircleGeometry\n\n function CircleGeometry( radius, segments, thetaStart, thetaLength ) {\n\n Geometry.call( this );\n\n this.type = 'CircleGeometry';\n\n this.parameters = {\n radius: radius,\n segments: segments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n this.fromBufferGeometry( new CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) );\n this.mergeVertices();\n\n }\n\n CircleGeometry.prototype = Object.create( Geometry.prototype );\n CircleGeometry.prototype.constructor = CircleGeometry;\n\n // CircleBufferGeometry\n\n function CircleBufferGeometry( radius, segments, thetaStart, thetaLength ) {\n\n BufferGeometry.call( this );\n\n this.type = 'CircleBufferGeometry';\n\n this.parameters = {\n radius: radius,\n segments: segments,\n thetaStart: thetaStart,\n thetaLength: thetaLength\n };\n\n radius = radius || 1;\n segments = segments !== undefined ? Math.max( 3, segments ) : 8;\n\n thetaStart = thetaStart !== undefined ? thetaStart : 0;\n thetaLength = thetaLength !== undefined ? thetaLength : Math.PI * 2;\n\n // buffers\n\n var indices = [];\n var vertices = [];\n var normals = [];\n var uvs = [];\n\n // helper variables\n\n var i, s;\n var vertex = new Vector3();\n var uv = new Vector2();\n\n // center point\n\n vertices.push( 0, 0, 0 );\n normals.push( 0, 0, 1 );\n uvs.push( 0.5, 0.5 );\n\n for ( s = 0, i = 3; s <= segments; s ++, i += 3 ) {\n\n var segment = thetaStart + s / segments * thetaLength;\n\n // vertex\n\n vertex.x = radius * Math.cos( segment );\n vertex.y = radius * Math.sin( segment );\n\n vertices.push( vertex.x, vertex.y, vertex.z );\n\n // normal\n\n normals.push( 0, 0, 1 );\n\n // uvs\n\n uv.x = ( vertices[ i ] / radius + 1 ) / 2;\n uv.y = ( vertices[ i + 1 ] / radius + 1 ) / 2;\n\n uvs.push( uv.x, uv.y );\n\n }\n\n // indices\n\n for ( i = 1; i <= segments; i ++ ) {\n\n indices.push( i, i + 1, 0 );\n\n }\n\n // build geometry\n\n this.setIndex( indices );\n this.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n this.addAttribute( 'normal', new Float32BufferAttribute( normals, 3 ) );\n this.addAttribute( 'uv', new Float32BufferAttribute( uvs, 2 ) );\n\n }\n\n CircleBufferGeometry.prototype = Object.create( BufferGeometry.prototype );\n CircleBufferGeometry.prototype.constructor = CircleBufferGeometry;\n\n\n\n var Geometries = Object.freeze({\n WireframeGeometry: WireframeGeometry,\n ParametricGeometry: ParametricGeometry,\n ParametricBufferGeometry: ParametricBufferGeometry,\n TetrahedronGeometry: TetrahedronGeometry,\n TetrahedronBufferGeometry: TetrahedronBufferGeometry,\n OctahedronGeometry: OctahedronGeometry,\n OctahedronBufferGeometry: OctahedronBufferGeometry,\n IcosahedronGeometry: IcosahedronGeometry,\n IcosahedronBufferGeometry: IcosahedronBufferGeometry,\n DodecahedronGeometry: DodecahedronGeometry,\n DodecahedronBufferGeometry: DodecahedronBufferGeometry,\n PolyhedronGeometry: PolyhedronGeometry,\n PolyhedronBufferGeometry: PolyhedronBufferGeometry,\n TubeGeometry: TubeGeometry,\n TubeBufferGeometry: TubeBufferGeometry,\n TorusKnotGeometry: TorusKnotGeometry,\n TorusKnotBufferGeometry: TorusKnotBufferGeometry,\n TorusGeometry: TorusGeometry,\n TorusBufferGeometry: TorusBufferGeometry,\n TextGeometry: TextGeometry,\n TextBufferGeometry: TextBufferGeometry,\n SphereGeometry: SphereGeometry,\n SphereBufferGeometry: SphereBufferGeometry,\n RingGeometry: RingGeometry,\n RingBufferGeometry: RingBufferGeometry,\n PlaneGeometry: PlaneGeometry,\n PlaneBufferGeometry: PlaneBufferGeometry,\n LatheGeometry: LatheGeometry,\n LatheBufferGeometry: LatheBufferGeometry,\n ShapeGeometry: ShapeGeometry,\n ShapeBufferGeometry: ShapeBufferGeometry,\n ExtrudeGeometry: ExtrudeGeometry,\n ExtrudeBufferGeometry: ExtrudeBufferGeometry,\n EdgesGeometry: EdgesGeometry,\n ConeGeometry: ConeGeometry,\n ConeBufferGeometry: ConeBufferGeometry,\n CylinderGeometry: CylinderGeometry,\n CylinderBufferGeometry: CylinderBufferGeometry,\n CircleGeometry: CircleGeometry,\n CircleBufferGeometry: CircleBufferGeometry,\n BoxGeometry: BoxGeometry,\n BoxBufferGeometry: BoxBufferGeometry\n });\n\n /**\n * @author mrdoob / http://mrdoob.com/\n *\n * parameters = {\n * color: <THREE.Color>\n * }\n */\n\n function ShadowMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'ShadowMaterial';\n\n this.color = new Color( 0x000000 );\n this.transparent = true;\n\n this.setValues( parameters );\n\n }\n\n ShadowMaterial.prototype = Object.create( Material.prototype );\n ShadowMaterial.prototype.constructor = ShadowMaterial;\n\n ShadowMaterial.prototype.isShadowMaterial = true;\n\n ShadowMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function RawShaderMaterial( parameters ) {\n\n ShaderMaterial.call( this, parameters );\n\n this.type = 'RawShaderMaterial';\n\n }\n\n RawShaderMaterial.prototype = Object.create( ShaderMaterial.prototype );\n RawShaderMaterial.prototype.constructor = RawShaderMaterial;\n\n RawShaderMaterial.prototype.isRawShaderMaterial = true;\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * color: <hex>,\n * roughness: <float>,\n * metalness: <float>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * roughnessMap: new THREE.Texture( <Image> ),\n *\n * metalnessMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * envMapIntensity: <float>\n *\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshStandardMaterial( parameters ) {\n\n Material.call( this );\n\n this.defines = { 'STANDARD': '' };\n\n this.type = 'MeshStandardMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n this.roughness = 0.5;\n this.metalness = 0.5;\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.roughnessMap = null;\n\n this.metalnessMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.envMapIntensity = 1.0;\n\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshStandardMaterial.prototype = Object.create( Material.prototype );\n MeshStandardMaterial.prototype.constructor = MeshStandardMaterial;\n\n MeshStandardMaterial.prototype.isMeshStandardMaterial = true;\n\n MeshStandardMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.defines = { 'STANDARD': '' };\n\n this.color.copy( source.color );\n this.roughness = source.roughness;\n this.metalness = source.metalness;\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.roughnessMap = source.roughnessMap;\n\n this.metalnessMap = source.metalnessMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.envMapIntensity = source.envMapIntensity;\n\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * reflectivity: <float>\n * }\n */\n\n function MeshPhysicalMaterial( parameters ) {\n\n MeshStandardMaterial.call( this );\n\n this.defines = { 'PHYSICAL': '' };\n\n this.type = 'MeshPhysicalMaterial';\n\n this.reflectivity = 0.5; // maps to F0 = 0.04\n\n this.clearCoat = 0.0;\n this.clearCoatRoughness = 0.0;\n\n this.setValues( parameters );\n\n }\n\n MeshPhysicalMaterial.prototype = Object.create( MeshStandardMaterial.prototype );\n MeshPhysicalMaterial.prototype.constructor = MeshPhysicalMaterial;\n\n MeshPhysicalMaterial.prototype.isMeshPhysicalMaterial = true;\n\n MeshPhysicalMaterial.prototype.copy = function ( source ) {\n\n MeshStandardMaterial.prototype.copy.call( this, source );\n\n this.defines = { 'PHYSICAL': '' };\n\n this.reflectivity = source.reflectivity;\n\n this.clearCoat = source.clearCoat;\n this.clearCoatRoughness = source.clearCoatRoughness;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * specular: <hex>,\n * shininess: <float>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshPhongMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshPhongMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n this.specular = new Color( 0x111111 );\n this.shininess = 30;\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshPhongMaterial.prototype = Object.create( Material.prototype );\n MeshPhongMaterial.prototype.constructor = MeshPhongMaterial;\n\n MeshPhongMaterial.prototype.isMeshPhongMaterial = true;\n\n MeshPhongMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.specular.copy( source.specular );\n this.shininess = source.shininess;\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author takahirox / http://github.com/takahirox\n *\n * parameters = {\n * gradientMap: new THREE.Texture( <Image> )\n * }\n */\n\n function MeshToonMaterial( parameters ) {\n\n MeshPhongMaterial.call( this );\n\n this.defines = { 'TOON': '' };\n\n this.type = 'MeshToonMaterial';\n\n this.gradientMap = null;\n\n this.setValues( parameters );\n\n }\n\n MeshToonMaterial.prototype = Object.create( MeshPhongMaterial.prototype );\n MeshToonMaterial.prototype.constructor = MeshToonMaterial;\n\n MeshToonMaterial.prototype.isMeshToonMaterial = true;\n\n MeshToonMaterial.prototype.copy = function ( source ) {\n\n MeshPhongMaterial.prototype.copy.call( this, source );\n\n this.gradientMap = source.gradientMap;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n *\n * parameters = {\n * opacity: <float>,\n *\n * bumpMap: new THREE.Texture( <Image> ),\n * bumpScale: <float>,\n *\n * normalMap: new THREE.Texture( <Image> ),\n * normalScale: <Vector2>,\n *\n * displacementMap: new THREE.Texture( <Image> ),\n * displacementScale: <float>,\n * displacementBias: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshNormalMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshNormalMaterial';\n\n this.bumpMap = null;\n this.bumpScale = 1;\n\n this.normalMap = null;\n this.normalScale = new Vector2( 1, 1 );\n\n this.displacementMap = null;\n this.displacementScale = 1;\n this.displacementBias = 0;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n\n this.fog = false;\n this.lights = false;\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshNormalMaterial.prototype = Object.create( Material.prototype );\n MeshNormalMaterial.prototype.constructor = MeshNormalMaterial;\n\n MeshNormalMaterial.prototype.isMeshNormalMaterial = true;\n\n MeshNormalMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.bumpMap = source.bumpMap;\n this.bumpScale = source.bumpScale;\n\n this.normalMap = source.normalMap;\n this.normalScale.copy( source.normalScale );\n\n this.displacementMap = source.displacementMap;\n this.displacementScale = source.displacementScale;\n this.displacementBias = source.displacementBias;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * map: new THREE.Texture( <Image> ),\n *\n * lightMap: new THREE.Texture( <Image> ),\n * lightMapIntensity: <float>\n *\n * aoMap: new THREE.Texture( <Image> ),\n * aoMapIntensity: <float>\n *\n * emissive: <hex>,\n * emissiveIntensity: <float>\n * emissiveMap: new THREE.Texture( <Image> ),\n *\n * specularMap: new THREE.Texture( <Image> ),\n *\n * alphaMap: new THREE.Texture( <Image> ),\n *\n * envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ),\n * combine: THREE.Multiply,\n * reflectivity: <float>,\n * refractionRatio: <float>,\n *\n * wireframe: <boolean>,\n * wireframeLinewidth: <float>,\n *\n * skinning: <bool>,\n * morphTargets: <bool>,\n * morphNormals: <bool>\n * }\n */\n\n function MeshLambertMaterial( parameters ) {\n\n Material.call( this );\n\n this.type = 'MeshLambertMaterial';\n\n this.color = new Color( 0xffffff ); // diffuse\n\n this.map = null;\n\n this.lightMap = null;\n this.lightMapIntensity = 1.0;\n\n this.aoMap = null;\n this.aoMapIntensity = 1.0;\n\n this.emissive = new Color( 0x000000 );\n this.emissiveIntensity = 1.0;\n this.emissiveMap = null;\n\n this.specularMap = null;\n\n this.alphaMap = null;\n\n this.envMap = null;\n this.combine = MultiplyOperation;\n this.reflectivity = 1;\n this.refractionRatio = 0.98;\n\n this.wireframe = false;\n this.wireframeLinewidth = 1;\n this.wireframeLinecap = 'round';\n this.wireframeLinejoin = 'round';\n\n this.skinning = false;\n this.morphTargets = false;\n this.morphNormals = false;\n\n this.setValues( parameters );\n\n }\n\n MeshLambertMaterial.prototype = Object.create( Material.prototype );\n MeshLambertMaterial.prototype.constructor = MeshLambertMaterial;\n\n MeshLambertMaterial.prototype.isMeshLambertMaterial = true;\n\n MeshLambertMaterial.prototype.copy = function ( source ) {\n\n Material.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n\n this.map = source.map;\n\n this.lightMap = source.lightMap;\n this.lightMapIntensity = source.lightMapIntensity;\n\n this.aoMap = source.aoMap;\n this.aoMapIntensity = source.aoMapIntensity;\n\n this.emissive.copy( source.emissive );\n this.emissiveMap = source.emissiveMap;\n this.emissiveIntensity = source.emissiveIntensity;\n\n this.specularMap = source.specularMap;\n\n this.alphaMap = source.alphaMap;\n\n this.envMap = source.envMap;\n this.combine = source.combine;\n this.reflectivity = source.reflectivity;\n this.refractionRatio = source.refractionRatio;\n\n this.wireframe = source.wireframe;\n this.wireframeLinewidth = source.wireframeLinewidth;\n this.wireframeLinecap = source.wireframeLinecap;\n this.wireframeLinejoin = source.wireframeLinejoin;\n\n this.skinning = source.skinning;\n this.morphTargets = source.morphTargets;\n this.morphNormals = source.morphNormals;\n\n return this;\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n *\n * parameters = {\n * color: <hex>,\n * opacity: <float>,\n *\n * linewidth: <float>,\n *\n * scale: <float>,\n * dashSize: <float>,\n * gapSize: <float>\n * }\n */\n\n function LineDashedMaterial( parameters ) {\n\n LineBasicMaterial.call( this );\n\n this.type = 'LineDashedMaterial';\n\n this.scale = 1;\n this.dashSize = 3;\n this.gapSize = 1;\n\n this.setValues( parameters );\n\n }\n\n LineDashedMaterial.prototype = Object.create( LineBasicMaterial.prototype );\n LineDashedMaterial.prototype.constructor = LineDashedMaterial;\n\n LineDashedMaterial.prototype.isLineDashedMaterial = true;\n\n LineDashedMaterial.prototype.copy = function ( source ) {\n\n LineBasicMaterial.prototype.copy.call( this, source );\n\n this.scale = source.scale;\n this.dashSize = source.dashSize;\n this.gapSize = source.gapSize;\n\n return this;\n\n };\n\n\n\n var Materials = Object.freeze({\n ShadowMaterial: ShadowMaterial,\n SpriteMaterial: SpriteMaterial,\n RawShaderMaterial: RawShaderMaterial,\n ShaderMaterial: ShaderMaterial,\n PointsMaterial: PointsMaterial,\n MeshPhysicalMaterial: MeshPhysicalMaterial,\n MeshStandardMaterial: MeshStandardMaterial,\n MeshPhongMaterial: MeshPhongMaterial,\n MeshToonMaterial: MeshToonMaterial,\n MeshNormalMaterial: MeshNormalMaterial,\n MeshLambertMaterial: MeshLambertMaterial,\n MeshDepthMaterial: MeshDepthMaterial,\n MeshDistanceMaterial: MeshDistanceMaterial,\n MeshBasicMaterial: MeshBasicMaterial,\n LineDashedMaterial: LineDashedMaterial,\n LineBasicMaterial: LineBasicMaterial,\n Material: Material\n });\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var Cache = {\n\n enabled: false,\n\n files: {},\n\n add: function ( key, file ) {\n\n if ( this.enabled === false ) return;\n\n // console.log( 'THREE.Cache', 'Adding key:', key );\n\n this.files[ key ] = file;\n\n },\n\n get: function ( key ) {\n\n if ( this.enabled === false ) return;\n\n // console.log( 'THREE.Cache', 'Checking key:', key );\n\n return this.files[ key ];\n\n },\n\n remove: function ( key ) {\n\n delete this.files[ key ];\n\n },\n\n clear: function () {\n\n this.files = {};\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LoadingManager( onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var isLoading = false;\n var itemsLoaded = 0;\n var itemsTotal = 0;\n var urlModifier = undefined;\n\n this.onStart = undefined;\n this.onLoad = onLoad;\n this.onProgress = onProgress;\n this.onError = onError;\n\n this.itemStart = function ( url ) {\n\n itemsTotal ++;\n\n if ( isLoading === false ) {\n\n if ( scope.onStart !== undefined ) {\n\n scope.onStart( url, itemsLoaded, itemsTotal );\n\n }\n\n }\n\n isLoading = true;\n\n };\n\n this.itemEnd = function ( url ) {\n\n itemsLoaded ++;\n\n if ( scope.onProgress !== undefined ) {\n\n scope.onProgress( url, itemsLoaded, itemsTotal );\n\n }\n\n if ( itemsLoaded === itemsTotal ) {\n\n isLoading = false;\n\n if ( scope.onLoad !== undefined ) {\n\n scope.onLoad();\n\n }\n\n }\n\n };\n\n this.itemError = function ( url ) {\n\n if ( scope.onError !== undefined ) {\n\n scope.onError( url );\n\n }\n\n };\n\n this.resolveURL = function ( url ) {\n\n if ( urlModifier ) {\n\n return urlModifier( url );\n\n }\n\n return url;\n\n };\n\n this.setURLModifier = function ( transform ) {\n\n urlModifier = transform;\n return this;\n\n };\n\n }\n\n var DefaultLoadingManager = new LoadingManager();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var loading = {};\n\n function FileLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( FileLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n url = this.manager.resolveURL( url );\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n // Check if request is duplicate\n\n if ( loading[ url ] !== undefined ) {\n\n loading[ url ].push( {\n\n onLoad: onLoad,\n onProgress: onProgress,\n onError: onError\n\n } );\n\n return;\n\n }\n\n // Check for data: URI\n var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;\n var dataUriRegexResult = url.match( dataUriRegex );\n\n // Safari can not handle Data URIs through XMLHttpRequest so process manually\n if ( dataUriRegexResult ) {\n\n var mimeType = dataUriRegexResult[ 1 ];\n var isBase64 = !! dataUriRegexResult[ 2 ];\n var data = dataUriRegexResult[ 3 ];\n\n data = window.decodeURIComponent( data );\n\n if ( isBase64 ) data = window.atob( data );\n\n try {\n\n var response;\n var responseType = ( this.responseType || '' ).toLowerCase();\n\n switch ( responseType ) {\n\n case 'arraybuffer':\n case 'blob':\n\n var view = new Uint8Array( data.length );\n\n for ( var i = 0; i < data.length; i ++ ) {\n\n view[ i ] = data.charCodeAt( i );\n\n }\n\n if ( responseType === 'blob' ) {\n\n response = new Blob( [ view.buffer ], { type: mimeType } );\n\n } else {\n\n response = view.buffer;\n\n }\n\n break;\n\n case 'document':\n\n var parser = new DOMParser();\n response = parser.parseFromString( data, mimeType );\n\n break;\n\n case 'json':\n\n response = JSON.parse( data );\n\n break;\n\n default: // 'text' or other\n\n response = data;\n\n break;\n\n }\n\n // Wait for next browser tick like standard XMLHttpRequest event dispatching does\n window.setTimeout( function () {\n\n if ( onLoad ) onLoad( response );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n } catch ( error ) {\n\n // Wait for next browser tick like standard XMLHttpRequest event dispatching does\n window.setTimeout( function () {\n\n if ( onError ) onError( error );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, 0 );\n\n }\n\n } else {\n\n // Initialise array for duplicate requests\n\n loading[ url ] = [];\n\n loading[ url ].push( {\n\n onLoad: onLoad,\n onProgress: onProgress,\n onError: onError\n\n } );\n\n var request = new XMLHttpRequest();\n\n request.open( 'GET', url, true );\n\n request.addEventListener( 'load', function ( event ) {\n\n var response = this.response;\n\n Cache.add( url, response );\n\n var callbacks = loading[ url ];\n\n delete loading[ url ];\n\n if ( this.status === 200 ) {\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onLoad ) callback.onLoad( response );\n\n }\n\n scope.manager.itemEnd( url );\n\n } else if ( this.status === 0 ) {\n\n // Some browsers return HTTP Status 0 when using non-http protocol\n // e.g. 'file://' or 'data://'. Handle as success.\n\n console.warn( 'THREE.FileLoader: HTTP Status 0 received.' );\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onLoad ) callback.onLoad( response );\n\n }\n\n scope.manager.itemEnd( url );\n\n } else {\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onError ) callback.onError( event );\n\n }\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }\n\n }, false );\n\n request.addEventListener( 'progress', function ( event ) {\n\n var callbacks = loading[ url ];\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onProgress ) callback.onProgress( event );\n\n }\n\n }, false );\n\n request.addEventListener( 'error', function ( event ) {\n\n var callbacks = loading[ url ];\n\n delete loading[ url ];\n\n for ( var i = 0, il = callbacks.length; i < il; i ++ ) {\n\n var callback = callbacks[ i ];\n if ( callback.onError ) callback.onError( event );\n\n }\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, false );\n\n if ( this.responseType !== undefined ) request.responseType = this.responseType;\n if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials;\n\n if ( request.overrideMimeType ) request.overrideMimeType( this.mimeType !== undefined ? this.mimeType : 'text/plain' );\n\n for ( var header in this.requestHeader ) {\n\n request.setRequestHeader( header, this.requestHeader[ header ] );\n\n }\n\n request.send( null );\n\n }\n\n scope.manager.itemStart( url );\n\n return request;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n },\n\n setResponseType: function ( value ) {\n\n this.responseType = value;\n return this;\n\n },\n\n setWithCredentials: function ( value ) {\n\n this.withCredentials = value;\n return this;\n\n },\n\n setMimeType: function ( value ) {\n\n this.mimeType = value;\n return this;\n\n },\n\n setRequestHeader: function ( value ) {\n\n this.requestHeader = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n *\n * Abstract Base class to block based textures loader (dds, pvr, ...)\n */\n\n function CompressedTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n // override in sub classes\n this._parser = null;\n\n }\n\n Object.assign( CompressedTextureLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var images = [];\n\n var texture = new CompressedTexture();\n texture.image = images;\n\n var loader = new FileLoader( this.manager );\n loader.setPath( this.path );\n loader.setResponseType( 'arraybuffer' );\n\n function loadTexture( i ) {\n\n loader.load( url[ i ], function ( buffer ) {\n\n var texDatas = scope._parser( buffer, true );\n\n images[ i ] = {\n width: texDatas.width,\n height: texDatas.height,\n format: texDatas.format,\n mipmaps: texDatas.mipmaps\n };\n\n loaded += 1;\n\n if ( loaded === 6 ) {\n\n if ( texDatas.mipmapCount === 1 )\n texture.minFilter = LinearFilter;\n\n texture.format = texDatas.format;\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }\n\n }, onProgress, onError );\n\n }\n\n if ( Array.isArray( url ) ) {\n\n var loaded = 0;\n\n for ( var i = 0, il = url.length; i < il; ++ i ) {\n\n loadTexture( i );\n\n }\n\n } else {\n\n // compressed cubemap texture stored in a single DDS file\n\n loader.load( url, function ( buffer ) {\n\n var texDatas = scope._parser( buffer, true );\n\n if ( texDatas.isCubemap ) {\n\n var faces = texDatas.mipmaps.length / texDatas.mipmapCount;\n\n for ( var f = 0; f < faces; f ++ ) {\n\n images[ f ] = { mipmaps: [] };\n\n for ( var i = 0; i < texDatas.mipmapCount; i ++ ) {\n\n images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] );\n images[ f ].format = texDatas.format;\n images[ f ].width = texDatas.width;\n images[ f ].height = texDatas.height;\n\n }\n\n }\n\n } else {\n\n texture.image.width = texDatas.width;\n texture.image.height = texDatas.height;\n texture.mipmaps = texDatas.mipmaps;\n\n }\n\n if ( texDatas.mipmapCount === 1 ) {\n\n texture.minFilter = LinearFilter;\n\n }\n\n texture.format = texDatas.format;\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }, onProgress, onError );\n\n }\n\n return texture;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author Nikos M. / https://github.com/foo123/\n *\n * Abstract Base class to load generic binary textures formats (rgbe, hdr, ...)\n */\n\n function DataTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n // override in sub classes\n this._parser = null;\n\n }\n\n Object.assign( DataTextureLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var texture = new DataTexture();\n\n var loader = new FileLoader( this.manager );\n loader.setResponseType( 'arraybuffer' );\n\n loader.load( url, function ( buffer ) {\n\n var texData = scope._parser( buffer );\n\n if ( ! texData ) return;\n\n if ( undefined !== texData.image ) {\n\n texture.image = texData.image;\n\n } else if ( undefined !== texData.data ) {\n\n texture.image.width = texData.width;\n texture.image.height = texData.height;\n texture.image.data = texData.data;\n\n }\n\n texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : ClampToEdgeWrapping;\n texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : ClampToEdgeWrapping;\n\n texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : LinearFilter;\n texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : LinearMipMapLinearFilter;\n\n texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1;\n\n if ( undefined !== texData.format ) {\n\n texture.format = texData.format;\n\n }\n if ( undefined !== texData.type ) {\n\n texture.type = texData.type;\n\n }\n\n if ( undefined !== texData.mipmaps ) {\n\n texture.mipmaps = texData.mipmaps;\n\n }\n\n if ( 1 === texData.mipmapCount ) {\n\n texture.minFilter = LinearFilter;\n\n }\n\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture, texData );\n\n }, onProgress, onError );\n\n\n return texture;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ImageLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( ImageLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n url = this.manager.resolveURL( url );\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n var image = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'img' );\n\n image.addEventListener( 'load', function () {\n\n Cache.add( url, this );\n\n if ( onLoad ) onLoad( this );\n\n scope.manager.itemEnd( url );\n\n }, false );\n\n /*\n image.addEventListener( 'progress', function ( event ) {\n\n if ( onProgress ) onProgress( event );\n\n }, false );\n */\n\n image.addEventListener( 'error', function ( event ) {\n\n if ( onError ) onError( event );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n }, false );\n\n if ( url.substr( 0, 5 ) !== 'data:' ) {\n\n if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin;\n\n }\n\n scope.manager.itemStart( url );\n\n image.src = url;\n\n return image;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function CubeTextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( CubeTextureLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( urls, onLoad, onProgress, onError ) {\n\n var texture = new CubeTexture();\n\n var loader = new ImageLoader( this.manager );\n loader.setCrossOrigin( this.crossOrigin );\n loader.setPath( this.path );\n\n var loaded = 0;\n\n function loadTexture( i ) {\n\n loader.load( urls[ i ], function ( image ) {\n\n texture.images[ i ] = image;\n\n loaded ++;\n\n if ( loaded === 6 ) {\n\n texture.needsUpdate = true;\n\n if ( onLoad ) onLoad( texture );\n\n }\n\n }, undefined, onError );\n\n }\n\n for ( var i = 0; i < urls.length; ++ i ) {\n\n loadTexture( i );\n\n }\n\n return texture;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function TextureLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( TextureLoader.prototype, {\n\n crossOrigin: 'Anonymous',\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var texture = new Texture();\n\n var loader = new ImageLoader( this.manager );\n loader.setCrossOrigin( this.crossOrigin );\n loader.setPath( this.path );\n\n loader.load( url, function ( image ) {\n\n texture.image = image;\n\n // JPEGs can't have an alpha channel, so memory can be saved by storing them as RGB.\n var isJPEG = url.search( /\\.(jpg|jpeg)$/ ) > 0 || url.search( /^data\\:image\\/jpeg/ ) === 0;\n\n texture.format = isJPEG ? RGBFormat : RGBAFormat;\n texture.needsUpdate = true;\n\n if ( onLoad !== undefined ) {\n\n onLoad( texture );\n\n }\n\n }, onProgress, onError );\n\n return texture;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Extensible curve object\n *\n * Some common of curve methods:\n * .getPoint( t, optionalTarget ), .getTangent( t )\n * .getPointAt( u, optionalTarget ), .getTangentAt( u )\n * .getPoints(), .getSpacedPoints()\n * .getLength()\n * .updateArcLengths()\n *\n * This following curves inherit from THREE.Curve:\n *\n * -- 2D curves --\n * THREE.ArcCurve\n * THREE.CubicBezierCurve\n * THREE.EllipseCurve\n * THREE.LineCurve\n * THREE.QuadraticBezierCurve\n * THREE.SplineCurve\n *\n * -- 3D curves --\n * THREE.CatmullRomCurve3\n * THREE.CubicBezierCurve3\n * THREE.LineCurve3\n * THREE.QuadraticBezierCurve3\n *\n * A series of curves can be represented as a THREE.CurvePath.\n *\n **/\n\n /**************************************************************\n * Abstract Curve base class\n **************************************************************/\n\n function Curve() {\n\n this.type = 'Curve';\n\n this.arcLengthDivisions = 200;\n\n }\n\n Object.assign( Curve.prototype, {\n\n // Virtual base class method to overwrite and implement in subclasses\n // - t [0 .. 1]\n\n getPoint: function ( /* t, optionalTarget */ ) {\n\n console.warn( 'THREE.Curve: .getPoint() not implemented.' );\n return null;\n\n },\n\n // Get point at relative position in curve according to arc length\n // - u [0 .. 1]\n\n getPointAt: function ( u, optionalTarget ) {\n\n var t = this.getUtoTmapping( u );\n return this.getPoint( t, optionalTarget );\n\n },\n\n // Get sequence of points using getPoint( t )\n\n getPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 5;\n\n var points = [];\n\n for ( var d = 0; d <= divisions; d ++ ) {\n\n points.push( this.getPoint( d / divisions ) );\n\n }\n\n return points;\n\n },\n\n // Get sequence of points using getPointAt( u )\n\n getSpacedPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 5;\n\n var points = [];\n\n for ( var d = 0; d <= divisions; d ++ ) {\n\n points.push( this.getPointAt( d / divisions ) );\n\n }\n\n return points;\n\n },\n\n // Get total curve arc length\n\n getLength: function () {\n\n var lengths = this.getLengths();\n return lengths[ lengths.length - 1 ];\n\n },\n\n // Get list of cumulative segment lengths\n\n getLengths: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = this.arcLengthDivisions;\n\n if ( this.cacheArcLengths &&\n ( this.cacheArcLengths.length === divisions + 1 ) &&\n ! this.needsUpdate ) {\n\n return this.cacheArcLengths;\n\n }\n\n this.needsUpdate = false;\n\n var cache = [];\n var current, last = this.getPoint( 0 );\n var p, sum = 0;\n\n cache.push( 0 );\n\n for ( p = 1; p <= divisions; p ++ ) {\n\n current = this.getPoint( p / divisions );\n sum += current.distanceTo( last );\n cache.push( sum );\n last = current;\n\n }\n\n this.cacheArcLengths = cache;\n\n return cache; // { sums: cache, sum: sum }; Sum is in the last element.\n\n },\n\n updateArcLengths: function () {\n\n this.needsUpdate = true;\n this.getLengths();\n\n },\n\n // Given u ( 0 .. 1 ), get a t to find p. This gives you points which are equidistant\n\n getUtoTmapping: function ( u, distance ) {\n\n var arcLengths = this.getLengths();\n\n var i = 0, il = arcLengths.length;\n\n var targetArcLength; // The targeted u distance value to get\n\n if ( distance ) {\n\n targetArcLength = distance;\n\n } else {\n\n targetArcLength = u * arcLengths[ il - 1 ];\n\n }\n\n // binary search for the index with largest value smaller than target u distance\n\n var low = 0, high = il - 1, comparison;\n\n while ( low <= high ) {\n\n i = Math.floor( low + ( high - low ) / 2 ); // less likely to overflow, though probably not issue here, JS doesn't really have integers, all numbers are floats\n\n comparison = arcLengths[ i ] - targetArcLength;\n\n if ( comparison < 0 ) {\n\n low = i + 1;\n\n } else if ( comparison > 0 ) {\n\n high = i - 1;\n\n } else {\n\n high = i;\n break;\n\n // DONE\n\n }\n\n }\n\n i = high;\n\n if ( arcLengths[ i ] === targetArcLength ) {\n\n return i / ( il - 1 );\n\n }\n\n // we could get finer grain at lengths, or use simple interpolation between two points\n\n var lengthBefore = arcLengths[ i ];\n var lengthAfter = arcLengths[ i + 1 ];\n\n var segmentLength = lengthAfter - lengthBefore;\n\n // determine where we are between the 'before' and 'after' points\n\n var segmentFraction = ( targetArcLength - lengthBefore ) / segmentLength;\n\n // add that fractional amount to t\n\n var t = ( i + segmentFraction ) / ( il - 1 );\n\n return t;\n\n },\n\n // Returns a unit vector tangent at t\n // In case any sub curve does not implement its tangent derivation,\n // 2 points a small delta apart will be used to find its gradient\n // which seems to give a reasonable approximation\n\n getTangent: function ( t ) {\n\n var delta = 0.0001;\n var t1 = t - delta;\n var t2 = t + delta;\n\n // Capping in case of danger\n\n if ( t1 < 0 ) t1 = 0;\n if ( t2 > 1 ) t2 = 1;\n\n var pt1 = this.getPoint( t1 );\n var pt2 = this.getPoint( t2 );\n\n var vec = pt2.clone().sub( pt1 );\n return vec.normalize();\n\n },\n\n getTangentAt: function ( u ) {\n\n var t = this.getUtoTmapping( u );\n return this.getTangent( t );\n\n },\n\n computeFrenetFrames: function ( segments, closed ) {\n\n // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf\n\n var normal = new Vector3();\n\n var tangents = [];\n var normals = [];\n var binormals = [];\n\n var vec = new Vector3();\n var mat = new Matrix4();\n\n var i, u, theta;\n\n // compute the tangent vectors for each segment on the curve\n\n for ( i = 0; i <= segments; i ++ ) {\n\n u = i / segments;\n\n tangents[ i ] = this.getTangentAt( u );\n tangents[ i ].normalize();\n\n }\n\n // select an initial normal vector perpendicular to the first tangent vector,\n // and in the direction of the minimum tangent xyz component\n\n normals[ 0 ] = new Vector3();\n binormals[ 0 ] = new Vector3();\n var min = Number.MAX_VALUE;\n var tx = Math.abs( tangents[ 0 ].x );\n var ty = Math.abs( tangents[ 0 ].y );\n var tz = Math.abs( tangents[ 0 ].z );\n\n if ( tx <= min ) {\n\n min = tx;\n normal.set( 1, 0, 0 );\n\n }\n\n if ( ty <= min ) {\n\n min = ty;\n normal.set( 0, 1, 0 );\n\n }\n\n if ( tz <= min ) {\n\n normal.set( 0, 0, 1 );\n\n }\n\n vec.crossVectors( tangents[ 0 ], normal ).normalize();\n\n normals[ 0 ].crossVectors( tangents[ 0 ], vec );\n binormals[ 0 ].crossVectors( tangents[ 0 ], normals[ 0 ] );\n\n\n // compute the slowly-varying normal and binormal vectors for each segment on the curve\n\n for ( i = 1; i <= segments; i ++ ) {\n\n normals[ i ] = normals[ i - 1 ].clone();\n\n binormals[ i ] = binormals[ i - 1 ].clone();\n\n vec.crossVectors( tangents[ i - 1 ], tangents[ i ] );\n\n if ( vec.length() > Number.EPSILON ) {\n\n vec.normalize();\n\n theta = Math.acos( _Math.clamp( tangents[ i - 1 ].dot( tangents[ i ] ), - 1, 1 ) ); // clamp for floating pt errors\n\n normals[ i ].applyMatrix4( mat.makeRotationAxis( vec, theta ) );\n\n }\n\n binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n }\n\n // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same\n\n if ( closed === true ) {\n\n theta = Math.acos( _Math.clamp( normals[ 0 ].dot( normals[ segments ] ), - 1, 1 ) );\n theta /= segments;\n\n if ( tangents[ 0 ].dot( vec.crossVectors( normals[ 0 ], normals[ segments ] ) ) > 0 ) {\n\n theta = - theta;\n\n }\n\n for ( i = 1; i <= segments; i ++ ) {\n\n // twist a little...\n normals[ i ].applyMatrix4( mat.makeRotationAxis( tangents[ i ], theta * i ) );\n binormals[ i ].crossVectors( tangents[ i ], normals[ i ] );\n\n }\n\n }\n\n return {\n tangents: tangents,\n normals: normals,\n binormals: binormals\n };\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( source ) {\n\n this.arcLengthDivisions = source.arcLengthDivisions;\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = {\n metadata: {\n version: 4.5,\n type: 'Curve',\n generator: 'Curve.toJSON'\n }\n };\n\n data.arcLengthDivisions = this.arcLengthDivisions;\n data.type = this.type;\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n this.arcLengthDivisions = json.arcLengthDivisions;\n\n return this;\n\n }\n\n } );\n\n function EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n Curve.call( this );\n\n this.type = 'EllipseCurve';\n\n this.aX = aX || 0;\n this.aY = aY || 0;\n\n this.xRadius = xRadius || 1;\n this.yRadius = yRadius || 1;\n\n this.aStartAngle = aStartAngle || 0;\n this.aEndAngle = aEndAngle || 2 * Math.PI;\n\n this.aClockwise = aClockwise || false;\n\n this.aRotation = aRotation || 0;\n\n }\n\n EllipseCurve.prototype = Object.create( Curve.prototype );\n EllipseCurve.prototype.constructor = EllipseCurve;\n\n EllipseCurve.prototype.isEllipseCurve = true;\n\n EllipseCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var twoPi = Math.PI * 2;\n var deltaAngle = this.aEndAngle - this.aStartAngle;\n var samePoints = Math.abs( deltaAngle ) < Number.EPSILON;\n\n // ensures that deltaAngle is 0 .. 2 PI\n while ( deltaAngle < 0 ) deltaAngle += twoPi;\n while ( deltaAngle > twoPi ) deltaAngle -= twoPi;\n\n if ( deltaAngle < Number.EPSILON ) {\n\n if ( samePoints ) {\n\n deltaAngle = 0;\n\n } else {\n\n deltaAngle = twoPi;\n\n }\n\n }\n\n if ( this.aClockwise === true && ! samePoints ) {\n\n if ( deltaAngle === twoPi ) {\n\n deltaAngle = - twoPi;\n\n } else {\n\n deltaAngle = deltaAngle - twoPi;\n\n }\n\n }\n\n var angle = this.aStartAngle + t * deltaAngle;\n var x = this.aX + this.xRadius * Math.cos( angle );\n var y = this.aY + this.yRadius * Math.sin( angle );\n\n if ( this.aRotation !== 0 ) {\n\n var cos = Math.cos( this.aRotation );\n var sin = Math.sin( this.aRotation );\n\n var tx = x - this.aX;\n var ty = y - this.aY;\n\n // Rotate the point about the center of the ellipse.\n x = tx * cos - ty * sin + this.aX;\n y = tx * sin + ty * cos + this.aY;\n\n }\n\n return point.set( x, y );\n\n };\n\n EllipseCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.aX = source.aX;\n this.aY = source.aY;\n\n this.xRadius = source.xRadius;\n this.yRadius = source.yRadius;\n\n this.aStartAngle = source.aStartAngle;\n this.aEndAngle = source.aEndAngle;\n\n this.aClockwise = source.aClockwise;\n\n this.aRotation = source.aRotation;\n\n return this;\n\n };\n\n\n EllipseCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.aX = this.aX;\n data.aY = this.aY;\n\n data.xRadius = this.xRadius;\n data.yRadius = this.yRadius;\n\n data.aStartAngle = this.aStartAngle;\n data.aEndAngle = this.aEndAngle;\n\n data.aClockwise = this.aClockwise;\n\n data.aRotation = this.aRotation;\n\n return data;\n\n };\n\n EllipseCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.aX = json.aX;\n this.aY = json.aY;\n\n this.xRadius = json.xRadius;\n this.yRadius = json.yRadius;\n\n this.aStartAngle = json.aStartAngle;\n this.aEndAngle = json.aEndAngle;\n\n this.aClockwise = json.aClockwise;\n\n this.aRotation = json.aRotation;\n\n return this;\n\n };\n\n function ArcCurve( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n EllipseCurve.call( this, aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n this.type = 'ArcCurve';\n\n }\n\n ArcCurve.prototype = Object.create( EllipseCurve.prototype );\n ArcCurve.prototype.constructor = ArcCurve;\n\n ArcCurve.prototype.isArcCurve = true;\n\n /**\n * @author zz85 https://github.com/zz85\n *\n * Centripetal CatmullRom Curve - which is useful for avoiding\n * cusps and self-intersections in non-uniform catmull rom curves.\n * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf\n *\n * curve.type accepts centripetal(default), chordal and catmullrom\n * curve.tension is used for catmullrom which defaults to 0.5\n */\n\n\n /*\n Based on an optimized c++ solution in\n - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/\n - http://ideone.com/NoEbVM\n\n This CubicPoly class could be used for reusing some variables and calculations,\n but for three.js curve use, it could be possible inlined and flatten into a single function call\n which can be placed in CurveUtils.\n */\n\n function CubicPoly() {\n\n var c0 = 0, c1 = 0, c2 = 0, c3 = 0;\n\n /*\n * Compute coefficients for a cubic polynomial\n * p(s) = c0 + c1*s + c2*s^2 + c3*s^3\n * such that\n * p(0) = x0, p(1) = x1\n * and\n * p'(0) = t0, p'(1) = t1.\n */\n function init( x0, x1, t0, t1 ) {\n\n c0 = x0;\n c1 = t0;\n c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;\n c3 = 2 * x0 - 2 * x1 + t0 + t1;\n\n }\n\n return {\n\n initCatmullRom: function ( x0, x1, x2, x3, tension ) {\n\n init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );\n\n },\n\n initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {\n\n // compute tangents when parameterized in [t1,t2]\n var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;\n var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;\n\n // rescale tangents for parametrization in [0,1]\n t1 *= dt1;\n t2 *= dt1;\n\n init( x1, x2, t1, t2 );\n\n },\n\n calc: function ( t ) {\n\n var t2 = t * t;\n var t3 = t2 * t;\n return c0 + c1 * t + c2 * t2 + c3 * t3;\n\n }\n\n };\n\n }\n\n //\n\n var tmp = new Vector3();\n var px = new CubicPoly();\n var py = new CubicPoly();\n var pz = new CubicPoly();\n\n function CatmullRomCurve3( points, closed, curveType, tension ) {\n\n Curve.call( this );\n\n this.type = 'CatmullRomCurve3';\n\n this.points = points || [];\n this.closed = closed || false;\n this.curveType = curveType || 'centripetal';\n this.tension = tension || 0.5;\n\n }\n\n CatmullRomCurve3.prototype = Object.create( Curve.prototype );\n CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;\n\n CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;\n\n CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var points = this.points;\n var l = points.length;\n\n var p = ( l - ( this.closed ? 0 : 1 ) ) * t;\n var intPoint = Math.floor( p );\n var weight = p - intPoint;\n\n if ( this.closed ) {\n\n intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;\n\n } else if ( weight === 0 && intPoint === l - 1 ) {\n\n intPoint = l - 2;\n weight = 1;\n\n }\n\n var p0, p1, p2, p3; // 4 points\n\n if ( this.closed || intPoint > 0 ) {\n\n p0 = points[ ( intPoint - 1 ) % l ];\n\n } else {\n\n // extrapolate first point\n tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );\n p0 = tmp;\n\n }\n\n p1 = points[ intPoint % l ];\n p2 = points[ ( intPoint + 1 ) % l ];\n\n if ( this.closed || intPoint + 2 < l ) {\n\n p3 = points[ ( intPoint + 2 ) % l ];\n\n } else {\n\n // extrapolate last point\n tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );\n p3 = tmp;\n\n }\n\n if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {\n\n // init Centripetal / Chordal Catmull-Rom\n var pow = this.curveType === 'chordal' ? 0.5 : 0.25;\n var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );\n var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );\n var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );\n\n // safety check for repeated points\n if ( dt1 < 1e-4 ) dt1 = 1.0;\n if ( dt0 < 1e-4 ) dt0 = dt1;\n if ( dt2 < 1e-4 ) dt2 = dt1;\n\n px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );\n py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );\n pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );\n\n } else if ( this.curveType === 'catmullrom' ) {\n\n px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );\n py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );\n pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );\n\n }\n\n point.set(\n px.calc( weight ),\n py.calc( weight ),\n pz.calc( weight )\n );\n\n return point;\n\n };\n\n CatmullRomCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.points = [];\n\n for ( var i = 0, l = source.points.length; i < l; i ++ ) {\n\n var point = source.points[ i ];\n\n this.points.push( point.clone() );\n\n }\n\n this.closed = source.closed;\n this.curveType = source.curveType;\n this.tension = source.tension;\n\n return this;\n\n };\n\n CatmullRomCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.points = [];\n\n for ( var i = 0, l = this.points.length; i < l; i ++ ) {\n\n var point = this.points[ i ];\n data.points.push( point.toArray() );\n\n }\n\n data.closed = this.closed;\n data.curveType = this.curveType;\n data.tension = this.tension;\n\n return data;\n\n };\n\n CatmullRomCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.points = [];\n\n for ( var i = 0, l = json.points.length; i < l; i ++ ) {\n\n var point = json.points[ i ];\n this.points.push( new Vector3().fromArray( point ) );\n\n }\n\n this.closed = json.closed;\n this.curveType = json.curveType;\n this.tension = json.tension;\n\n return this;\n\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n * Bezier Curves formulas obtained from\n * http://en.wikipedia.org/wiki/Bézier_curve\n */\n\n function CatmullRom( t, p0, p1, p2, p3 ) {\n\n var v0 = ( p2 - p0 ) * 0.5;\n var v1 = ( p3 - p1 ) * 0.5;\n var t2 = t * t;\n var t3 = t * t2;\n return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1;\n\n }\n\n //\n\n function QuadraticBezierP0( t, p ) {\n\n var k = 1 - t;\n return k * k * p;\n\n }\n\n function QuadraticBezierP1( t, p ) {\n\n return 2 * ( 1 - t ) * t * p;\n\n }\n\n function QuadraticBezierP2( t, p ) {\n\n return t * t * p;\n\n }\n\n function QuadraticBezier( t, p0, p1, p2 ) {\n\n return QuadraticBezierP0( t, p0 ) + QuadraticBezierP1( t, p1 ) +\n QuadraticBezierP2( t, p2 );\n\n }\n\n //\n\n function CubicBezierP0( t, p ) {\n\n var k = 1 - t;\n return k * k * k * p;\n\n }\n\n function CubicBezierP1( t, p ) {\n\n var k = 1 - t;\n return 3 * k * k * t * p;\n\n }\n\n function CubicBezierP2( t, p ) {\n\n return 3 * ( 1 - t ) * t * t * p;\n\n }\n\n function CubicBezierP3( t, p ) {\n\n return t * t * t * p;\n\n }\n\n function CubicBezier( t, p0, p1, p2, p3 ) {\n\n return CubicBezierP0( t, p0 ) + CubicBezierP1( t, p1 ) + CubicBezierP2( t, p2 ) +\n CubicBezierP3( t, p3 );\n\n }\n\n function CubicBezierCurve( v0, v1, v2, v3 ) {\n\n Curve.call( this );\n\n this.type = 'CubicBezierCurve';\n\n this.v0 = v0 || new Vector2();\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n this.v3 = v3 || new Vector2();\n\n }\n\n CubicBezierCurve.prototype = Object.create( Curve.prototype );\n CubicBezierCurve.prototype.constructor = CubicBezierCurve;\n\n CubicBezierCurve.prototype.isCubicBezierCurve = true;\n\n CubicBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n point.set(\n CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n CubicBezier( t, v0.y, v1.y, v2.y, v3.y )\n );\n\n return point;\n\n };\n\n CubicBezierCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n this.v3.copy( source.v3 );\n\n return this;\n\n };\n\n CubicBezierCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n data.v3 = this.v3.toArray();\n\n return data;\n\n };\n\n CubicBezierCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n this.v3.fromArray( json.v3 );\n\n return this;\n\n };\n\n function CubicBezierCurve3( v0, v1, v2, v3 ) {\n\n Curve.call( this );\n\n this.type = 'CubicBezierCurve3';\n\n this.v0 = v0 || new Vector3();\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n this.v3 = v3 || new Vector3();\n\n }\n\n CubicBezierCurve3.prototype = Object.create( Curve.prototype );\n CubicBezierCurve3.prototype.constructor = CubicBezierCurve3;\n\n CubicBezierCurve3.prototype.isCubicBezierCurve3 = true;\n\n CubicBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2, v3 = this.v3;\n\n point.set(\n CubicBezier( t, v0.x, v1.x, v2.x, v3.x ),\n CubicBezier( t, v0.y, v1.y, v2.y, v3.y ),\n CubicBezier( t, v0.z, v1.z, v2.z, v3.z )\n );\n\n return point;\n\n };\n\n CubicBezierCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n this.v3.copy( source.v3 );\n\n return this;\n\n };\n\n CubicBezierCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n data.v3 = this.v3.toArray();\n\n return data;\n\n };\n\n CubicBezierCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n this.v3.fromArray( json.v3 );\n\n return this;\n\n };\n\n function LineCurve( v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'LineCurve';\n\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n\n }\n\n LineCurve.prototype = Object.create( Curve.prototype );\n LineCurve.prototype.constructor = LineCurve;\n\n LineCurve.prototype.isLineCurve = true;\n\n LineCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n if ( t === 1 ) {\n\n point.copy( this.v2 );\n\n } else {\n\n point.copy( this.v2 ).sub( this.v1 );\n point.multiplyScalar( t ).add( this.v1 );\n\n }\n\n return point;\n\n };\n\n // Line curve is linear, so we can overwrite default getPointAt\n\n LineCurve.prototype.getPointAt = function ( u, optionalTarget ) {\n\n return this.getPoint( u, optionalTarget );\n\n };\n\n LineCurve.prototype.getTangent = function ( /* t */ ) {\n\n var tangent = this.v2.clone().sub( this.v1 );\n\n return tangent.normalize();\n\n };\n\n LineCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n LineCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n LineCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function LineCurve3( v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'LineCurve3';\n\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n\n }\n\n LineCurve3.prototype = Object.create( Curve.prototype );\n LineCurve3.prototype.constructor = LineCurve3;\n\n LineCurve3.prototype.isLineCurve3 = true;\n\n LineCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n if ( t === 1 ) {\n\n point.copy( this.v2 );\n\n } else {\n\n point.copy( this.v2 ).sub( this.v1 );\n point.multiplyScalar( t ).add( this.v1 );\n\n }\n\n return point;\n\n };\n\n // Line curve is linear, so we can overwrite default getPointAt\n\n LineCurve3.prototype.getPointAt = function ( u, optionalTarget ) {\n\n return this.getPoint( u, optionalTarget );\n\n };\n\n LineCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n LineCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n LineCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function QuadraticBezierCurve( v0, v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'QuadraticBezierCurve';\n\n this.v0 = v0 || new Vector2();\n this.v1 = v1 || new Vector2();\n this.v2 = v2 || new Vector2();\n\n }\n\n QuadraticBezierCurve.prototype = Object.create( Curve.prototype );\n QuadraticBezierCurve.prototype.constructor = QuadraticBezierCurve;\n\n QuadraticBezierCurve.prototype.isQuadraticBezierCurve = true;\n\n QuadraticBezierCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n point.set(\n QuadraticBezier( t, v0.x, v1.x, v2.x ),\n QuadraticBezier( t, v0.y, v1.y, v2.y )\n );\n\n return point;\n\n };\n\n QuadraticBezierCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n QuadraticBezierCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n QuadraticBezierCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function QuadraticBezierCurve3( v0, v1, v2 ) {\n\n Curve.call( this );\n\n this.type = 'QuadraticBezierCurve3';\n\n this.v0 = v0 || new Vector3();\n this.v1 = v1 || new Vector3();\n this.v2 = v2 || new Vector3();\n\n }\n\n QuadraticBezierCurve3.prototype = Object.create( Curve.prototype );\n QuadraticBezierCurve3.prototype.constructor = QuadraticBezierCurve3;\n\n QuadraticBezierCurve3.prototype.isQuadraticBezierCurve3 = true;\n\n QuadraticBezierCurve3.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector3();\n\n var v0 = this.v0, v1 = this.v1, v2 = this.v2;\n\n point.set(\n QuadraticBezier( t, v0.x, v1.x, v2.x ),\n QuadraticBezier( t, v0.y, v1.y, v2.y ),\n QuadraticBezier( t, v0.z, v1.z, v2.z )\n );\n\n return point;\n\n };\n\n QuadraticBezierCurve3.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.v0.copy( source.v0 );\n this.v1.copy( source.v1 );\n this.v2.copy( source.v2 );\n\n return this;\n\n };\n\n QuadraticBezierCurve3.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.v0 = this.v0.toArray();\n data.v1 = this.v1.toArray();\n data.v2 = this.v2.toArray();\n\n return data;\n\n };\n\n QuadraticBezierCurve3.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.v0.fromArray( json.v0 );\n this.v1.fromArray( json.v1 );\n this.v2.fromArray( json.v2 );\n\n return this;\n\n };\n\n function SplineCurve( points /* array of Vector2 */ ) {\n\n Curve.call( this );\n\n this.type = 'SplineCurve';\n\n this.points = points || [];\n\n }\n\n SplineCurve.prototype = Object.create( Curve.prototype );\n SplineCurve.prototype.constructor = SplineCurve;\n\n SplineCurve.prototype.isSplineCurve = true;\n\n SplineCurve.prototype.getPoint = function ( t, optionalTarget ) {\n\n var point = optionalTarget || new Vector2();\n\n var points = this.points;\n var p = ( points.length - 1 ) * t;\n\n var intPoint = Math.floor( p );\n var weight = p - intPoint;\n\n var p0 = points[ intPoint === 0 ? intPoint : intPoint - 1 ];\n var p1 = points[ intPoint ];\n var p2 = points[ intPoint > points.length - 2 ? points.length - 1 : intPoint + 1 ];\n var p3 = points[ intPoint > points.length - 3 ? points.length - 1 : intPoint + 2 ];\n\n point.set(\n CatmullRom( weight, p0.x, p1.x, p2.x, p3.x ),\n CatmullRom( weight, p0.y, p1.y, p2.y, p3.y )\n );\n\n return point;\n\n };\n\n SplineCurve.prototype.copy = function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.points = [];\n\n for ( var i = 0, l = source.points.length; i < l; i ++ ) {\n\n var point = source.points[ i ];\n\n this.points.push( point.clone() );\n\n }\n\n return this;\n\n };\n\n SplineCurve.prototype.toJSON = function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.points = [];\n\n for ( var i = 0, l = this.points.length; i < l; i ++ ) {\n\n var point = this.points[ i ];\n data.points.push( point.toArray() );\n\n }\n\n return data;\n\n };\n\n SplineCurve.prototype.fromJSON = function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.points = [];\n\n for ( var i = 0, l = json.points.length; i < l; i ++ ) {\n\n var point = json.points[ i ];\n this.points.push( new Vector2().fromArray( point ) );\n\n }\n\n return this;\n\n };\n\n\n\n var Curves = Object.freeze({\n ArcCurve: ArcCurve,\n CatmullRomCurve3: CatmullRomCurve3,\n CubicBezierCurve: CubicBezierCurve,\n CubicBezierCurve3: CubicBezierCurve3,\n EllipseCurve: EllipseCurve,\n LineCurve: LineCurve,\n LineCurve3: LineCurve3,\n QuadraticBezierCurve: QuadraticBezierCurve,\n QuadraticBezierCurve3: QuadraticBezierCurve3,\n SplineCurve: SplineCurve\n });\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n *\n **/\n\n /**************************************************************\n * Curved Path - a curve path is simply a array of connected\n * curves, but retains the api of a curve\n **************************************************************/\n\n function CurvePath() {\n\n Curve.call( this );\n\n this.type = 'CurvePath';\n\n this.curves = [];\n this.autoClose = false; // Automatically closes the path\n\n }\n\n CurvePath.prototype = Object.assign( Object.create( Curve.prototype ), {\n\n constructor: CurvePath,\n\n add: function ( curve ) {\n\n this.curves.push( curve );\n\n },\n\n closePath: function () {\n\n // Add a line curve if start and end of lines are not connected\n var startPoint = this.curves[ 0 ].getPoint( 0 );\n var endPoint = this.curves[ this.curves.length - 1 ].getPoint( 1 );\n\n if ( ! startPoint.equals( endPoint ) ) {\n\n this.curves.push( new LineCurve( endPoint, startPoint ) );\n\n }\n\n },\n\n // To get accurate point with reference to\n // entire path distance at time t,\n // following has to be done:\n\n // 1. Length of each sub path have to be known\n // 2. Locate and identify type of curve\n // 3. Get t for the curve\n // 4. Return curve.getPointAt(t')\n\n getPoint: function ( t ) {\n\n var d = t * this.getLength();\n var curveLengths = this.getCurveLengths();\n var i = 0;\n\n // To think about boundaries points.\n\n while ( i < curveLengths.length ) {\n\n if ( curveLengths[ i ] >= d ) {\n\n var diff = curveLengths[ i ] - d;\n var curve = this.curves[ i ];\n\n var segmentLength = curve.getLength();\n var u = segmentLength === 0 ? 0 : 1 - diff / segmentLength;\n\n return curve.getPointAt( u );\n\n }\n\n i ++;\n\n }\n\n return null;\n\n // loop where sum != 0, sum > d , sum+1 <d\n\n },\n\n // We cannot use the default THREE.Curve getPoint() with getLength() because in\n // THREE.Curve, getLength() depends on getPoint() but in THREE.CurvePath\n // getPoint() depends on getLength\n\n getLength: function () {\n\n var lens = this.getCurveLengths();\n return lens[ lens.length - 1 ];\n\n },\n\n // cacheLengths must be recalculated.\n updateArcLengths: function () {\n\n this.needsUpdate = true;\n this.cacheLengths = null;\n this.getCurveLengths();\n\n },\n\n // Compute lengths and cache them\n // We cannot overwrite getLengths() because UtoT mapping uses it.\n\n getCurveLengths: function () {\n\n // We use cache values if curves and cache array are same length\n\n if ( this.cacheLengths && this.cacheLengths.length === this.curves.length ) {\n\n return this.cacheLengths;\n\n }\n\n // Get length of sub-curve\n // Push sums into cached array\n\n var lengths = [], sums = 0;\n\n for ( var i = 0, l = this.curves.length; i < l; i ++ ) {\n\n sums += this.curves[ i ].getLength();\n lengths.push( sums );\n\n }\n\n this.cacheLengths = lengths;\n\n return lengths;\n\n },\n\n getSpacedPoints: function ( divisions ) {\n\n if ( divisions === undefined ) divisions = 40;\n\n var points = [];\n\n for ( var i = 0; i <= divisions; i ++ ) {\n\n points.push( this.getPoint( i / divisions ) );\n\n }\n\n if ( this.autoClose ) {\n\n points.push( points[ 0 ] );\n\n }\n\n return points;\n\n },\n\n getPoints: function ( divisions ) {\n\n divisions = divisions || 12;\n\n var points = [], last;\n\n for ( var i = 0, curves = this.curves; i < curves.length; i ++ ) {\n\n var curve = curves[ i ];\n var resolution = ( curve && curve.isEllipseCurve ) ? divisions * 2\n : ( curve && curve.isLineCurve ) ? 1\n : ( curve && curve.isSplineCurve ) ? divisions * curve.points.length\n : divisions;\n\n var pts = curve.getPoints( resolution );\n\n for ( var j = 0; j < pts.length; j ++ ) {\n\n var point = pts[ j ];\n\n if ( last && last.equals( point ) ) continue; // ensures no consecutive points are duplicates\n\n points.push( point );\n last = point;\n\n }\n\n }\n\n if ( this.autoClose && points.length > 1 && ! points[ points.length - 1 ].equals( points[ 0 ] ) ) {\n\n points.push( points[ 0 ] );\n\n }\n\n return points;\n\n },\n\n copy: function ( source ) {\n\n Curve.prototype.copy.call( this, source );\n\n this.curves = [];\n\n for ( var i = 0, l = source.curves.length; i < l; i ++ ) {\n\n var curve = source.curves[ i ];\n\n this.curves.push( curve.clone() );\n\n }\n\n this.autoClose = source.autoClose;\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = Curve.prototype.toJSON.call( this );\n\n data.autoClose = this.autoClose;\n data.curves = [];\n\n for ( var i = 0, l = this.curves.length; i < l; i ++ ) {\n\n var curve = this.curves[ i ];\n data.curves.push( curve.toJSON() );\n\n }\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n Curve.prototype.fromJSON.call( this, json );\n\n this.autoClose = json.autoClose;\n this.curves = [];\n\n for ( var i = 0, l = json.curves.length; i < l; i ++ ) {\n\n var curve = json.curves[ i ];\n this.curves.push( new Curves[ curve.type ]().fromJSON( curve ) );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Creates free form 2d path using series of points, lines or curves.\n **/\n\n function Path( points ) {\n\n CurvePath.call( this );\n\n this.type = 'Path';\n\n this.currentPoint = new Vector2();\n\n if ( points ) {\n\n this.setFromPoints( points );\n\n }\n\n }\n\n Path.prototype = Object.assign( Object.create( CurvePath.prototype ), {\n\n constructor: Path,\n\n setFromPoints: function ( points ) {\n\n this.moveTo( points[ 0 ].x, points[ 0 ].y );\n\n for ( var i = 1, l = points.length; i < l; i ++ ) {\n\n this.lineTo( points[ i ].x, points[ i ].y );\n\n }\n\n },\n\n moveTo: function ( x, y ) {\n\n this.currentPoint.set( x, y ); // TODO consider referencing vectors instead of copying?\n\n },\n\n lineTo: function ( x, y ) {\n\n var curve = new LineCurve( this.currentPoint.clone(), new Vector2( x, y ) );\n this.curves.push( curve );\n\n this.currentPoint.set( x, y );\n\n },\n\n quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {\n\n var curve = new QuadraticBezierCurve(\n this.currentPoint.clone(),\n new Vector2( aCPx, aCPy ),\n new Vector2( aX, aY )\n );\n\n this.curves.push( curve );\n\n this.currentPoint.set( aX, aY );\n\n },\n\n bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n var curve = new CubicBezierCurve(\n this.currentPoint.clone(),\n new Vector2( aCP1x, aCP1y ),\n new Vector2( aCP2x, aCP2y ),\n new Vector2( aX, aY )\n );\n\n this.curves.push( curve );\n\n this.currentPoint.set( aX, aY );\n\n },\n\n splineThru: function ( pts /*Array of Vector*/ ) {\n\n var npts = [ this.currentPoint.clone() ].concat( pts );\n\n var curve = new SplineCurve( npts );\n this.curves.push( curve );\n\n this.currentPoint.copy( pts[ pts.length - 1 ] );\n\n },\n\n arc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n var x0 = this.currentPoint.x;\n var y0 = this.currentPoint.y;\n\n this.absarc( aX + x0, aY + y0, aRadius,\n aStartAngle, aEndAngle, aClockwise );\n\n },\n\n absarc: function ( aX, aY, aRadius, aStartAngle, aEndAngle, aClockwise ) {\n\n this.absellipse( aX, aY, aRadius, aRadius, aStartAngle, aEndAngle, aClockwise );\n\n },\n\n ellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n var x0 = this.currentPoint.x;\n var y0 = this.currentPoint.y;\n\n this.absellipse( aX + x0, aY + y0, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n },\n\n absellipse: function ( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation ) {\n\n var curve = new EllipseCurve( aX, aY, xRadius, yRadius, aStartAngle, aEndAngle, aClockwise, aRotation );\n\n if ( this.curves.length > 0 ) {\n\n // if a previous curve is present, attempt to join\n var firstPoint = curve.getPoint( 0 );\n\n if ( ! firstPoint.equals( this.currentPoint ) ) {\n\n this.lineTo( firstPoint.x, firstPoint.y );\n\n }\n\n }\n\n this.curves.push( curve );\n\n var lastPoint = curve.getPoint( 1 );\n this.currentPoint.copy( lastPoint );\n\n },\n\n copy: function ( source ) {\n\n CurvePath.prototype.copy.call( this, source );\n\n this.currentPoint.copy( source.currentPoint );\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = CurvePath.prototype.toJSON.call( this );\n\n data.currentPoint = this.currentPoint.toArray();\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n CurvePath.prototype.fromJSON.call( this, json );\n\n this.currentPoint.fromArray( json.currentPoint );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * Defines a 2d shape plane using paths.\n **/\n\n // STEP 1 Create a path.\n // STEP 2 Turn path into shape.\n // STEP 3 ExtrudeGeometry takes in Shape/Shapes\n // STEP 3a - Extract points from each shape, turn to vertices\n // STEP 3b - Triangulate each shape, add faces.\n\n function Shape( points ) {\n\n Path.call( this, points );\n\n this.uuid = _Math.generateUUID();\n\n this.type = 'Shape';\n\n this.holes = [];\n\n }\n\n Shape.prototype = Object.assign( Object.create( Path.prototype ), {\n\n constructor: Shape,\n\n getPointsHoles: function ( divisions ) {\n\n var holesPts = [];\n\n for ( var i = 0, l = this.holes.length; i < l; i ++ ) {\n\n holesPts[ i ] = this.holes[ i ].getPoints( divisions );\n\n }\n\n return holesPts;\n\n },\n\n // get points of shape and holes (keypoints based on segments parameter)\n\n extractPoints: function ( divisions ) {\n\n return {\n\n shape: this.getPoints( divisions ),\n holes: this.getPointsHoles( divisions )\n\n };\n\n },\n\n copy: function ( source ) {\n\n Path.prototype.copy.call( this, source );\n\n this.holes = [];\n\n for ( var i = 0, l = source.holes.length; i < l; i ++ ) {\n\n var hole = source.holes[ i ];\n\n this.holes.push( hole.clone() );\n\n }\n\n return this;\n\n },\n\n toJSON: function () {\n\n var data = Path.prototype.toJSON.call( this );\n\n data.uuid = this.uuid;\n data.holes = [];\n\n for ( var i = 0, l = this.holes.length; i < l; i ++ ) {\n\n var hole = this.holes[ i ];\n data.holes.push( hole.toJSON() );\n\n }\n\n return data;\n\n },\n\n fromJSON: function ( json ) {\n\n Path.prototype.fromJSON.call( this, json );\n\n this.uuid = json.uuid;\n this.holes = [];\n\n for ( var i = 0, l = json.holes.length; i < l; i ++ ) {\n\n var hole = json.holes[ i ];\n this.holes.push( new Path().fromJSON( hole ) );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Light( color, intensity ) {\n\n Object3D.call( this );\n\n this.type = 'Light';\n\n this.color = new Color( color );\n this.intensity = intensity !== undefined ? intensity : 1;\n\n this.receiveShadow = undefined;\n\n }\n\n Light.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Light,\n\n isLight: true,\n\n copy: function ( source ) {\n\n Object3D.prototype.copy.call( this, source );\n\n this.color.copy( source.color );\n this.intensity = source.intensity;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Object3D.prototype.toJSON.call( this, meta );\n\n data.object.color = this.color.getHex();\n data.object.intensity = this.intensity;\n\n if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex();\n\n if ( this.distance !== undefined ) data.object.distance = this.distance;\n if ( this.angle !== undefined ) data.object.angle = this.angle;\n if ( this.decay !== undefined ) data.object.decay = this.decay;\n if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra;\n\n if ( this.shadow !== undefined ) data.object.shadow = this.shadow.toJSON();\n\n return data;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function HemisphereLight( skyColor, groundColor, intensity ) {\n\n Light.call( this, skyColor, intensity );\n\n this.type = 'HemisphereLight';\n\n this.castShadow = undefined;\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.groundColor = new Color( groundColor );\n\n }\n\n HemisphereLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: HemisphereLight,\n\n isHemisphereLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.groundColor.copy( source.groundColor );\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function LightShadow( camera ) {\n\n this.camera = camera;\n\n this.bias = 0;\n this.radius = 1;\n\n this.mapSize = new Vector2( 512, 512 );\n\n this.map = null;\n this.matrix = new Matrix4();\n\n }\n\n Object.assign( LightShadow.prototype, {\n\n copy: function ( source ) {\n\n this.camera = source.camera.clone();\n\n this.bias = source.bias;\n this.radius = source.radius;\n\n this.mapSize.copy( source.mapSize );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n toJSON: function () {\n\n var object = {};\n\n if ( this.bias !== 0 ) object.bias = this.bias;\n if ( this.radius !== 1 ) object.radius = this.radius;\n if ( this.mapSize.x !== 512 || this.mapSize.y !== 512 ) object.mapSize = this.mapSize.toArray();\n\n object.camera = this.camera.toJSON( false ).object;\n delete object.camera.matrix;\n\n return object;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function SpotLightShadow() {\n\n LightShadow.call( this, new PerspectiveCamera( 50, 1, 0.5, 500 ) );\n\n }\n\n SpotLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n constructor: SpotLightShadow,\n\n isSpotLightShadow: true,\n\n update: function ( light ) {\n\n var camera = this.camera;\n\n var fov = _Math.RAD2DEG * 2 * light.angle;\n var aspect = this.mapSize.width / this.mapSize.height;\n var far = light.distance || camera.far;\n\n if ( fov !== camera.fov || aspect !== camera.aspect || far !== camera.far ) {\n\n camera.fov = fov;\n camera.aspect = aspect;\n camera.far = far;\n camera.updateProjectionMatrix();\n\n }\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function SpotLight( color, intensity, distance, angle, penumbra, decay ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'SpotLight';\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.target = new Object3D();\n\n Object.defineProperty( this, 'power', {\n get: function () {\n\n // intensity = power per solid angle.\n // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n return this.intensity * Math.PI;\n\n },\n set: function ( power ) {\n\n // intensity = power per solid angle.\n // ref: equation (17) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n this.intensity = power / Math.PI;\n\n }\n } );\n\n this.distance = ( distance !== undefined ) ? distance : 0;\n this.angle = ( angle !== undefined ) ? angle : Math.PI / 3;\n this.penumbra = ( penumbra !== undefined ) ? penumbra : 0;\n this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.\n\n this.shadow = new SpotLightShadow();\n\n }\n\n SpotLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: SpotLight,\n\n isSpotLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.distance = source.distance;\n this.angle = source.angle;\n this.penumbra = source.penumbra;\n this.decay = source.decay;\n\n this.target = source.target.clone();\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n\n function PointLight( color, intensity, distance, decay ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'PointLight';\n\n Object.defineProperty( this, 'power', {\n get: function () {\n\n // intensity = power per solid angle.\n // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n return this.intensity * 4 * Math.PI;\n\n },\n set: function ( power ) {\n\n // intensity = power per solid angle.\n // ref: equation (15) from https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf\n this.intensity = power / ( 4 * Math.PI );\n\n }\n } );\n\n this.distance = ( distance !== undefined ) ? distance : 0;\n this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2.\n\n this.shadow = new LightShadow( new PerspectiveCamera( 90, 1, 0.5, 500 ) );\n\n }\n\n PointLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: PointLight,\n\n isPointLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.distance = source.distance;\n this.decay = source.decay;\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function DirectionalLightShadow( ) {\n\n LightShadow.call( this, new OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) );\n\n }\n\n DirectionalLightShadow.prototype = Object.assign( Object.create( LightShadow.prototype ), {\n\n constructor: DirectionalLightShadow\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function DirectionalLight( color, intensity ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'DirectionalLight';\n\n this.position.copy( Object3D.DefaultUp );\n this.updateMatrix();\n\n this.target = new Object3D();\n\n this.shadow = new DirectionalLightShadow();\n\n }\n\n DirectionalLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: DirectionalLight,\n\n isDirectionalLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.target = source.target.clone();\n\n this.shadow = source.shadow.clone();\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AmbientLight( color, intensity ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'AmbientLight';\n\n this.castShadow = undefined;\n\n }\n\n AmbientLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: AmbientLight,\n\n isAmbientLight: true\n\n } );\n\n /**\n * @author abelnation / http://github.com/abelnation\n */\n\n function RectAreaLight( color, intensity, width, height ) {\n\n Light.call( this, color, intensity );\n\n this.type = 'RectAreaLight';\n\n this.width = ( width !== undefined ) ? width : 10;\n this.height = ( height !== undefined ) ? height : 10;\n\n }\n\n RectAreaLight.prototype = Object.assign( Object.create( Light.prototype ), {\n\n constructor: RectAreaLight,\n\n isRectAreaLight: true,\n\n copy: function ( source ) {\n\n Light.prototype.copy.call( this, source );\n\n this.width = source.width;\n this.height = source.height;\n\n return this;\n\n },\n\n toJSON: function ( meta ) {\n\n var data = Light.prototype.toJSON.call( this, meta );\n\n data.object.width = this.width;\n data.object.height = this.height;\n\n return data;\n\n }\n\n } );\n\n /**\n *\n * A Track that interpolates Strings\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function StringKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n StringKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: StringKeyframeTrack,\n\n ValueTypeName: 'string',\n ValueBufferType: Array,\n\n DefaultInterpolation: InterpolateDiscrete,\n\n InterpolantFactoryMethodLinear: undefined,\n\n InterpolantFactoryMethodSmooth: undefined\n\n } );\n\n /**\n *\n * A Track of Boolean keyframe values.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function BooleanKeyframeTrack( name, times, values ) {\n\n KeyframeTrack.call( this, name, times, values );\n\n }\n\n BooleanKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: BooleanKeyframeTrack,\n\n ValueTypeName: 'bool',\n ValueBufferType: Array,\n\n DefaultInterpolation: InterpolateDiscrete,\n\n InterpolantFactoryMethodLinear: undefined,\n InterpolantFactoryMethodSmooth: undefined\n\n // Note: Actually this track could have a optimized / compressed\n // representation of a single value and a custom interpolant that\n // computes \"firstValue ^ isOdd( index )\".\n\n } );\n\n /**\n * Abstract base class of interpolants over parametric samples.\n *\n * The parameter domain is one dimensional, typically the time or a path\n * along a curve defined by the data.\n *\n * The sample values can have any dimensionality and derived classes may\n * apply special interpretations to the data.\n *\n * This class provides the interval seek in a Template Method, deferring\n * the actual interpolation to derived classes.\n *\n * Time complexity is O(1) for linear access crossing at most two points\n * and O(log N) for random access, where N is the number of positions.\n *\n * References:\n *\n * http://www.oodesign.com/template-method-pattern.html\n *\n * @author tschw\n */\n\n function Interpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n this.parameterPositions = parameterPositions;\n this._cachedIndex = 0;\n\n this.resultBuffer = resultBuffer !== undefined ?\n resultBuffer : new sampleValues.constructor( sampleSize );\n this.sampleValues = sampleValues;\n this.valueSize = sampleSize;\n\n }\n\n Object.assign( Interpolant.prototype, {\n\n evaluate: function ( t ) {\n\n var pp = this.parameterPositions,\n i1 = this._cachedIndex,\n\n t1 = pp[ i1 ],\n t0 = pp[ i1 - 1 ];\n\n validate_interval: {\n\n seek: {\n\n var right;\n\n linear_scan: {\n\n //- See http://jsperf.com/comparison-to-undefined/3\n //- slower code:\n //-\n //- if ( t >= t1 || t1 === undefined ) {\n forward_scan: if ( ! ( t < t1 ) ) {\n\n for ( var giveUpAt = i1 + 2; ; ) {\n\n if ( t1 === undefined ) {\n\n if ( t < t0 ) break forward_scan;\n\n // after end\n\n i1 = pp.length;\n this._cachedIndex = i1;\n return this.afterEnd_( i1 - 1, t, t0 );\n\n }\n\n if ( i1 === giveUpAt ) break; // this loop\n\n t0 = t1;\n t1 = pp[ ++ i1 ];\n\n if ( t < t1 ) {\n\n // we have arrived at the sought interval\n break seek;\n\n }\n\n }\n\n // prepare binary search on the right side of the index\n right = pp.length;\n break linear_scan;\n\n }\n\n //- slower code:\n //- if ( t < t0 || t0 === undefined ) {\n if ( ! ( t >= t0 ) ) {\n\n // looping?\n\n var t1global = pp[ 1 ];\n\n if ( t < t1global ) {\n\n i1 = 2; // + 1, using the scan for the details\n t0 = t1global;\n\n }\n\n // linear reverse scan\n\n for ( var giveUpAt = i1 - 2; ; ) {\n\n if ( t0 === undefined ) {\n\n // before start\n\n this._cachedIndex = 0;\n return this.beforeStart_( 0, t, t1 );\n\n }\n\n if ( i1 === giveUpAt ) break; // this loop\n\n t1 = t0;\n t0 = pp[ -- i1 - 1 ];\n\n if ( t >= t0 ) {\n\n // we have arrived at the sought interval\n break seek;\n\n }\n\n }\n\n // prepare binary search on the left side of the index\n right = i1;\n i1 = 0;\n break linear_scan;\n\n }\n\n // the interval is valid\n\n break validate_interval;\n\n } // linear scan\n\n // binary search\n\n while ( i1 < right ) {\n\n var mid = ( i1 + right ) >>> 1;\n\n if ( t < pp[ mid ] ) {\n\n right = mid;\n\n } else {\n\n i1 = mid + 1;\n\n }\n\n }\n\n t1 = pp[ i1 ];\n t0 = pp[ i1 - 1 ];\n\n // check boundary cases, again\n\n if ( t0 === undefined ) {\n\n this._cachedIndex = 0;\n return this.beforeStart_( 0, t, t1 );\n\n }\n\n if ( t1 === undefined ) {\n\n i1 = pp.length;\n this._cachedIndex = i1;\n return this.afterEnd_( i1 - 1, t0, t );\n\n }\n\n } // seek\n\n this._cachedIndex = i1;\n\n this.intervalChanged_( i1, t0, t1 );\n\n } // validate_interval\n\n return this.interpolate_( i1, t0, t, t1 );\n\n },\n\n settings: null, // optional, subclass-specific settings structure\n // Note: The indirection allows central control of many interpolants.\n\n // --- Protected interface\n\n DefaultSettings_: {},\n\n getSettings_: function () {\n\n return this.settings || this.DefaultSettings_;\n\n },\n\n copySampleValue_: function ( index ) {\n\n // copies a sample value to the result buffer\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n offset = index * stride;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] = values[ offset + i ];\n\n }\n\n return result;\n\n },\n\n // Template methods for derived classes:\n\n interpolate_: function ( /* i1, t0, t, t1 */ ) {\n\n throw new Error( 'call to abstract method' );\n // implementations shall return this.resultBuffer\n\n },\n\n intervalChanged_: function ( /* i1, t0, t1 */ ) {\n\n // empty\n\n }\n\n } );\n\n //!\\ DECLARE ALIAS AFTER assign prototype !\n Object.assign( Interpolant.prototype, {\n\n //( 0, t, t0 ), returns this.resultBuffer\n beforeStart_: Interpolant.prototype.copySampleValue_,\n\n //( N-1, tN-1, t ), returns this.resultBuffer\n afterEnd_: Interpolant.prototype.copySampleValue_,\n\n } );\n\n /**\n * Spherical linear unit quaternion interpolant.\n *\n * @author tschw\n */\n\n function QuaternionLinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n QuaternionLinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: QuaternionLinearInterpolant,\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n offset = i1 * stride,\n\n alpha = ( t - t0 ) / ( t1 - t0 );\n\n for ( var end = offset + stride; offset !== end; offset += 4 ) {\n\n Quaternion.slerpFlat( result, 0, values, offset - stride, values, offset, alpha );\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n *\n * A Track of quaternion keyframe values.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function QuaternionKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n QuaternionKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: QuaternionKeyframeTrack,\n\n ValueTypeName: 'quaternion',\n\n // ValueBufferType is inherited\n\n DefaultInterpolation: InterpolateLinear,\n\n InterpolantFactoryMethodLinear: function ( result ) {\n\n return new QuaternionLinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodSmooth: undefined // not yet implemented\n\n } );\n\n /**\n *\n * A Track of keyframe values that represent color.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function ColorKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n ColorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: ColorKeyframeTrack,\n\n ValueTypeName: 'color'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n // Note: Very basic implementation and nothing special yet.\n // However, this is the place for color space parameterization.\n\n } );\n\n /**\n *\n * A Track of numeric keyframe values.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function NumberKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n NumberKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: NumberKeyframeTrack,\n\n ValueTypeName: 'number'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n } );\n\n /**\n * Fast and simple cubic spline interpolant.\n *\n * It was derived from a Hermitian construction setting the first derivative\n * at each sample position to the linear slope between neighboring positions\n * over their parameter interval.\n *\n * @author tschw\n */\n\n function CubicInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n this._weightPrev = - 0;\n this._offsetPrev = - 0;\n this._weightNext = - 0;\n this._offsetNext = - 0;\n\n }\n\n CubicInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: CubicInterpolant,\n\n DefaultSettings_: {\n\n endingStart: ZeroCurvatureEnding,\n endingEnd: ZeroCurvatureEnding\n\n },\n\n intervalChanged_: function ( i1, t0, t1 ) {\n\n var pp = this.parameterPositions,\n iPrev = i1 - 2,\n iNext = i1 + 1,\n\n tPrev = pp[ iPrev ],\n tNext = pp[ iNext ];\n\n if ( tPrev === undefined ) {\n\n switch ( this.getSettings_().endingStart ) {\n\n case ZeroSlopeEnding:\n\n // f'(t0) = 0\n iPrev = i1;\n tPrev = 2 * t0 - t1;\n\n break;\n\n case WrapAroundEnding:\n\n // use the other end of the curve\n iPrev = pp.length - 2;\n tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ];\n\n break;\n\n default: // ZeroCurvatureEnding\n\n // f''(t0) = 0 a.k.a. Natural Spline\n iPrev = i1;\n tPrev = t1;\n\n }\n\n }\n\n if ( tNext === undefined ) {\n\n switch ( this.getSettings_().endingEnd ) {\n\n case ZeroSlopeEnding:\n\n // f'(tN) = 0\n iNext = i1;\n tNext = 2 * t1 - t0;\n\n break;\n\n case WrapAroundEnding:\n\n // use the other end of the curve\n iNext = 1;\n tNext = t1 + pp[ 1 ] - pp[ 0 ];\n\n break;\n\n default: // ZeroCurvatureEnding\n\n // f''(tN) = 0, a.k.a. Natural Spline\n iNext = i1 - 1;\n tNext = t0;\n\n }\n\n }\n\n var halfDt = ( t1 - t0 ) * 0.5,\n stride = this.valueSize;\n\n this._weightPrev = halfDt / ( t0 - tPrev );\n this._weightNext = halfDt / ( tNext - t1 );\n this._offsetPrev = iPrev * stride;\n this._offsetNext = iNext * stride;\n\n },\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n o1 = i1 * stride, o0 = o1 - stride,\n oP = this._offsetPrev, oN = this._offsetNext,\n wP = this._weightPrev, wN = this._weightNext,\n\n p = ( t - t0 ) / ( t1 - t0 ),\n pp = p * p,\n ppp = pp * p;\n\n // evaluate polynomials\n\n var sP = - wP * ppp + 2 * wP * pp - wP * p;\n var s0 = ( 1 + wP ) * ppp + ( - 1.5 - 2 * wP ) * pp + ( - 0.5 + wP ) * p + 1;\n var s1 = ( - 1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p;\n var sN = wN * ppp - wN * pp;\n\n // combine data linearly\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] =\n sP * values[ oP + i ] +\n s0 * values[ o0 + i ] +\n s1 * values[ o1 + i ] +\n sN * values[ oN + i ];\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n * @author tschw\n */\n\n function LinearInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n LinearInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: LinearInterpolant,\n\n interpolate_: function ( i1, t0, t, t1 ) {\n\n var result = this.resultBuffer,\n values = this.sampleValues,\n stride = this.valueSize,\n\n offset1 = i1 * stride,\n offset0 = offset1 - stride,\n\n weight1 = ( t - t0 ) / ( t1 - t0 ),\n weight0 = 1 - weight1;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n result[ i ] =\n values[ offset0 + i ] * weight0 +\n values[ offset1 + i ] * weight1;\n\n }\n\n return result;\n\n }\n\n } );\n\n /**\n *\n * Interpolant that evaluates to the sample value at the position preceeding\n * the parameter.\n *\n * @author tschw\n */\n\n function DiscreteInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {\n\n Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );\n\n }\n\n DiscreteInterpolant.prototype = Object.assign( Object.create( Interpolant.prototype ), {\n\n constructor: DiscreteInterpolant,\n\n interpolate_: function ( i1 /*, t0, t, t1 */ ) {\n\n return this.copySampleValue_( i1 - 1 );\n\n }\n\n } );\n\n /**\n * @author tschw\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n */\n\n var AnimationUtils = {\n\n // same as Array.prototype.slice, but also works on typed arrays\n arraySlice: function ( array, from, to ) {\n\n if ( AnimationUtils.isTypedArray( array ) ) {\n\n // in ios9 array.subarray(from, undefined) will return empty array\n // but array.subarray(from) or array.subarray(from, len) is correct\n return new array.constructor( array.subarray( from, to !== undefined ? to : array.length ) );\n\n }\n\n return array.slice( from, to );\n\n },\n\n // converts an array to a specific type\n convertArray: function ( array, type, forceClone ) {\n\n if ( ! array || // let 'undefined' and 'null' pass\n ! forceClone && array.constructor === type ) return array;\n\n if ( typeof type.BYTES_PER_ELEMENT === 'number' ) {\n\n return new type( array ); // create typed array\n\n }\n\n return Array.prototype.slice.call( array ); // create Array\n\n },\n\n isTypedArray: function ( object ) {\n\n return ArrayBuffer.isView( object ) &&\n ! ( object instanceof DataView );\n\n },\n\n // returns an array by which times and values can be sorted\n getKeyframeOrder: function ( times ) {\n\n function compareTime( i, j ) {\n\n return times[ i ] - times[ j ];\n\n }\n\n var n = times.length;\n var result = new Array( n );\n for ( var i = 0; i !== n; ++ i ) result[ i ] = i;\n\n result.sort( compareTime );\n\n return result;\n\n },\n\n // uses the array previously returned by 'getKeyframeOrder' to sort data\n sortedArray: function ( values, stride, order ) {\n\n var nValues = values.length;\n var result = new values.constructor( nValues );\n\n for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) {\n\n var srcOffset = order[ i ] * stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n result[ dstOffset ++ ] = values[ srcOffset + j ];\n\n }\n\n }\n\n return result;\n\n },\n\n // function for parsing AOS keyframe formats\n flattenJSON: function ( jsonKeys, times, values, valuePropertyName ) {\n\n var i = 1, key = jsonKeys[ 0 ];\n\n while ( key !== undefined && key[ valuePropertyName ] === undefined ) {\n\n key = jsonKeys[ i ++ ];\n\n }\n\n if ( key === undefined ) return; // no data\n\n var value = key[ valuePropertyName ];\n if ( value === undefined ) return; // no data\n\n if ( Array.isArray( value ) ) {\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n values.push.apply( values, value ); // push all elements\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n } else if ( value.toArray !== undefined ) {\n\n // ...assume THREE.Math-ish\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n value.toArray( values, values.length );\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n } else {\n\n // otherwise push as-is\n\n do {\n\n value = key[ valuePropertyName ];\n\n if ( value !== undefined ) {\n\n times.push( key.time );\n values.push( value );\n\n }\n\n key = jsonKeys[ i ++ ];\n\n } while ( key !== undefined );\n\n }\n\n }\n\n };\n\n /**\n *\n * A timed sequence of keyframes for a specific property.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function KeyframeTrack( name, times, values, interpolation ) {\n\n if ( name === undefined ) throw new Error( 'THREE.KeyframeTrack: track name is undefined' );\n if ( times === undefined || times.length === 0 ) throw new Error( 'THREE.KeyframeTrack: no keyframes in track named ' + name );\n\n this.name = name;\n\n this.times = AnimationUtils.convertArray( times, this.TimeBufferType );\n this.values = AnimationUtils.convertArray( values, this.ValueBufferType );\n\n this.setInterpolation( interpolation || this.DefaultInterpolation );\n\n this.validate();\n this.optimize();\n\n }\n\n // Static methods:\n\n Object.assign( KeyframeTrack, {\n\n // Serialization (in static context, because of constructor invocation\n // and automatic invocation of .toJSON):\n\n parse: function ( json ) {\n\n if ( json.type === undefined ) {\n\n throw new Error( 'THREE.KeyframeTrack: track type undefined, can not parse' );\n\n }\n\n var trackType = KeyframeTrack._getTrackTypeForValueTypeName( json.type );\n\n if ( json.times === undefined ) {\n\n var times = [], values = [];\n\n AnimationUtils.flattenJSON( json.keys, times, values, 'value' );\n\n json.times = times;\n json.values = values;\n\n }\n\n // derived classes can define a static parse method\n if ( trackType.parse !== undefined ) {\n\n return trackType.parse( json );\n\n } else {\n\n // by default, we assume a constructor compatible with the base\n return new trackType( json.name, json.times, json.values, json.interpolation );\n\n }\n\n },\n\n toJSON: function ( track ) {\n\n var trackType = track.constructor;\n\n var json;\n\n // derived classes can define a static toJSON method\n if ( trackType.toJSON !== undefined ) {\n\n json = trackType.toJSON( track );\n\n } else {\n\n // by default, we assume the data can be serialized as-is\n json = {\n\n 'name': track.name,\n 'times': AnimationUtils.convertArray( track.times, Array ),\n 'values': AnimationUtils.convertArray( track.values, Array )\n\n };\n\n var interpolation = track.getInterpolation();\n\n if ( interpolation !== track.DefaultInterpolation ) {\n\n json.interpolation = interpolation;\n\n }\n\n }\n\n json.type = track.ValueTypeName; // mandatory\n\n return json;\n\n },\n\n _getTrackTypeForValueTypeName: function ( typeName ) {\n\n switch ( typeName.toLowerCase() ) {\n\n case 'scalar':\n case 'double':\n case 'float':\n case 'number':\n case 'integer':\n\n return NumberKeyframeTrack;\n\n case 'vector':\n case 'vector2':\n case 'vector3':\n case 'vector4':\n\n return VectorKeyframeTrack;\n\n case 'color':\n\n return ColorKeyframeTrack;\n\n case 'quaternion':\n\n return QuaternionKeyframeTrack;\n\n case 'bool':\n case 'boolean':\n\n return BooleanKeyframeTrack;\n\n case 'string':\n\n return StringKeyframeTrack;\n\n }\n\n throw new Error( 'THREE.KeyframeTrack: Unsupported typeName: ' + typeName );\n\n }\n\n } );\n\n Object.assign( KeyframeTrack.prototype, {\n\n constructor: KeyframeTrack,\n\n TimeBufferType: Float32Array,\n\n ValueBufferType: Float32Array,\n\n DefaultInterpolation: InterpolateLinear,\n\n InterpolantFactoryMethodDiscrete: function ( result ) {\n\n return new DiscreteInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodLinear: function ( result ) {\n\n return new LinearInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n InterpolantFactoryMethodSmooth: function ( result ) {\n\n return new CubicInterpolant( this.times, this.values, this.getValueSize(), result );\n\n },\n\n setInterpolation: function ( interpolation ) {\n\n var factoryMethod;\n\n switch ( interpolation ) {\n\n case InterpolateDiscrete:\n\n factoryMethod = this.InterpolantFactoryMethodDiscrete;\n\n break;\n\n case InterpolateLinear:\n\n factoryMethod = this.InterpolantFactoryMethodLinear;\n\n break;\n\n case InterpolateSmooth:\n\n factoryMethod = this.InterpolantFactoryMethodSmooth;\n\n break;\n\n }\n\n if ( factoryMethod === undefined ) {\n\n var message = \"unsupported interpolation for \" +\n this.ValueTypeName + \" keyframe track named \" + this.name;\n\n if ( this.createInterpolant === undefined ) {\n\n // fall back to default, unless the default itself is messed up\n if ( interpolation !== this.DefaultInterpolation ) {\n\n this.setInterpolation( this.DefaultInterpolation );\n\n } else {\n\n throw new Error( message ); // fatal, in this case\n\n }\n\n }\n\n console.warn( 'THREE.KeyframeTrack:', message );\n return;\n\n }\n\n this.createInterpolant = factoryMethod;\n\n },\n\n getInterpolation: function () {\n\n switch ( this.createInterpolant ) {\n\n case this.InterpolantFactoryMethodDiscrete:\n\n return InterpolateDiscrete;\n\n case this.InterpolantFactoryMethodLinear:\n\n return InterpolateLinear;\n\n case this.InterpolantFactoryMethodSmooth:\n\n return InterpolateSmooth;\n\n }\n\n },\n\n getValueSize: function () {\n\n return this.values.length / this.times.length;\n\n },\n\n // move all keyframes either forwards or backwards in time\n shift: function ( timeOffset ) {\n\n if ( timeOffset !== 0.0 ) {\n\n var times = this.times;\n\n for ( var i = 0, n = times.length; i !== n; ++ i ) {\n\n times[ i ] += timeOffset;\n\n }\n\n }\n\n return this;\n\n },\n\n // scale all keyframe times by a factor (useful for frame <-> seconds conversions)\n scale: function ( timeScale ) {\n\n if ( timeScale !== 1.0 ) {\n\n var times = this.times;\n\n for ( var i = 0, n = times.length; i !== n; ++ i ) {\n\n times[ i ] *= timeScale;\n\n }\n\n }\n\n return this;\n\n },\n\n // removes keyframes before and after animation without changing any values within the range [startTime, endTime].\n // IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values\n trim: function ( startTime, endTime ) {\n\n var times = this.times,\n nKeys = times.length,\n from = 0,\n to = nKeys - 1;\n\n while ( from !== nKeys && times[ from ] < startTime ) {\n\n ++ from;\n\n }\n\n while ( to !== - 1 && times[ to ] > endTime ) {\n\n -- to;\n\n }\n\n ++ to; // inclusive -> exclusive bound\n\n if ( from !== 0 || to !== nKeys ) {\n\n // empty tracks are forbidden, so keep at least one keyframe\n if ( from >= to ) to = Math.max( to, 1 ), from = to - 1;\n\n var stride = this.getValueSize();\n this.times = AnimationUtils.arraySlice( times, from, to );\n this.values = AnimationUtils.arraySlice( this.values, from * stride, to * stride );\n\n }\n\n return this;\n\n },\n\n // ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable\n validate: function () {\n\n var valid = true;\n\n var valueSize = this.getValueSize();\n if ( valueSize - Math.floor( valueSize ) !== 0 ) {\n\n console.error( 'THREE.KeyframeTrack: Invalid value size in track.', this );\n valid = false;\n\n }\n\n var times = this.times,\n values = this.values,\n\n nKeys = times.length;\n\n if ( nKeys === 0 ) {\n\n console.error( 'THREE.KeyframeTrack: Track is empty.', this );\n valid = false;\n\n }\n\n var prevTime = null;\n\n for ( var i = 0; i !== nKeys; i ++ ) {\n\n var currTime = times[ i ];\n\n if ( typeof currTime === 'number' && isNaN( currTime ) ) {\n\n console.error( 'THREE.KeyframeTrack: Time is not a valid number.', this, i, currTime );\n valid = false;\n break;\n\n }\n\n if ( prevTime !== null && prevTime > currTime ) {\n\n console.error( 'THREE.KeyframeTrack: Out of order keys.', this, i, currTime, prevTime );\n valid = false;\n break;\n\n }\n\n prevTime = currTime;\n\n }\n\n if ( values !== undefined ) {\n\n if ( AnimationUtils.isTypedArray( values ) ) {\n\n for ( var i = 0, n = values.length; i !== n; ++ i ) {\n\n var value = values[ i ];\n\n if ( isNaN( value ) ) {\n\n console.error( 'THREE.KeyframeTrack: Value is not a valid number.', this, i, value );\n valid = false;\n break;\n\n }\n\n }\n\n }\n\n }\n\n return valid;\n\n },\n\n // removes equivalent sequential keys as common in morph target sequences\n // (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0)\n optimize: function () {\n\n var times = this.times,\n values = this.values,\n stride = this.getValueSize(),\n\n smoothInterpolation = this.getInterpolation() === InterpolateSmooth,\n\n writeIndex = 1,\n lastIndex = times.length - 1;\n\n for ( var i = 1; i < lastIndex; ++ i ) {\n\n var keep = false;\n\n var time = times[ i ];\n var timeNext = times[ i + 1 ];\n\n // remove adjacent keyframes scheduled at the same time\n\n if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) {\n\n if ( ! smoothInterpolation ) {\n\n // remove unnecessary keyframes same as their neighbors\n\n var offset = i * stride,\n offsetP = offset - stride,\n offsetN = offset + stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n var value = values[ offset + j ];\n\n if ( value !== values[ offsetP + j ] ||\n value !== values[ offsetN + j ] ) {\n\n keep = true;\n break;\n\n }\n\n }\n\n } else {\n\n keep = true;\n\n }\n\n }\n\n // in-place compaction\n\n if ( keep ) {\n\n if ( i !== writeIndex ) {\n\n times[ writeIndex ] = times[ i ];\n\n var readOffset = i * stride,\n writeOffset = writeIndex * stride;\n\n for ( var j = 0; j !== stride; ++ j ) {\n\n values[ writeOffset + j ] = values[ readOffset + j ];\n\n }\n\n }\n\n ++ writeIndex;\n\n }\n\n }\n\n // flush last keyframe (compaction looks ahead)\n\n if ( lastIndex > 0 ) {\n\n times[ writeIndex ] = times[ lastIndex ];\n\n for ( var readOffset = lastIndex * stride, writeOffset = writeIndex * stride, j = 0; j !== stride; ++ j ) {\n\n values[ writeOffset + j ] = values[ readOffset + j ];\n\n }\n\n ++ writeIndex;\n\n }\n\n if ( writeIndex !== times.length ) {\n\n this.times = AnimationUtils.arraySlice( times, 0, writeIndex );\n this.values = AnimationUtils.arraySlice( values, 0, writeIndex * stride );\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n *\n * A Track of vectored keyframe values.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function VectorKeyframeTrack( name, times, values, interpolation ) {\n\n KeyframeTrack.call( this, name, times, values, interpolation );\n\n }\n\n VectorKeyframeTrack.prototype = Object.assign( Object.create( KeyframeTrack.prototype ), {\n\n constructor: VectorKeyframeTrack,\n\n ValueTypeName: 'vector'\n\n // ValueBufferType is inherited\n\n // DefaultInterpolation is inherited\n\n } );\n\n /**\n *\n * Reusable set of Tracks that represent an animation.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n */\n\n function AnimationClip( name, duration, tracks ) {\n\n this.name = name;\n this.tracks = tracks;\n this.duration = ( duration !== undefined ) ? duration : - 1;\n\n this.uuid = _Math.generateUUID();\n\n // this means it should figure out its duration by scanning the tracks\n if ( this.duration < 0 ) {\n\n this.resetDuration();\n\n }\n\n this.optimize();\n\n }\n\n Object.assign( AnimationClip, {\n\n parse: function ( json ) {\n\n var tracks = [],\n jsonTracks = json.tracks,\n frameTime = 1.0 / ( json.fps || 1.0 );\n\n for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) {\n\n tracks.push( KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) );\n\n }\n\n return new AnimationClip( json.name, json.duration, tracks );\n\n },\n\n toJSON: function ( clip ) {\n\n var tracks = [],\n clipTracks = clip.tracks;\n\n var json = {\n\n 'name': clip.name,\n 'duration': clip.duration,\n 'tracks': tracks\n\n };\n\n for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) {\n\n tracks.push( KeyframeTrack.toJSON( clipTracks[ i ] ) );\n\n }\n\n return json;\n\n },\n\n CreateFromMorphTargetSequence: function ( name, morphTargetSequence, fps, noLoop ) {\n\n var numMorphTargets = morphTargetSequence.length;\n var tracks = [];\n\n for ( var i = 0; i < numMorphTargets; i ++ ) {\n\n var times = [];\n var values = [];\n\n times.push(\n ( i + numMorphTargets - 1 ) % numMorphTargets,\n i,\n ( i + 1 ) % numMorphTargets );\n\n values.push( 0, 1, 0 );\n\n var order = AnimationUtils.getKeyframeOrder( times );\n times = AnimationUtils.sortedArray( times, 1, order );\n values = AnimationUtils.sortedArray( values, 1, order );\n\n // if there is a key at the first frame, duplicate it as the\n // last frame as well for perfect loop.\n if ( ! noLoop && times[ 0 ] === 0 ) {\n\n times.push( numMorphTargets );\n values.push( values[ 0 ] );\n\n }\n\n tracks.push(\n new NumberKeyframeTrack(\n '.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']',\n times, values\n ).scale( 1.0 / fps ) );\n\n }\n\n return new AnimationClip( name, - 1, tracks );\n\n },\n\n findByName: function ( objectOrClipArray, name ) {\n\n var clipArray = objectOrClipArray;\n\n if ( ! Array.isArray( objectOrClipArray ) ) {\n\n var o = objectOrClipArray;\n clipArray = o.geometry && o.geometry.animations || o.animations;\n\n }\n\n for ( var i = 0; i < clipArray.length; i ++ ) {\n\n if ( clipArray[ i ].name === name ) {\n\n return clipArray[ i ];\n\n }\n\n }\n\n return null;\n\n },\n\n CreateClipsFromMorphTargetSequences: function ( morphTargets, fps, noLoop ) {\n\n var animationToMorphTargets = {};\n\n // tested with https://regex101.com/ on trick sequences\n // such flamingo_flyA_003, flamingo_run1_003, crdeath0059\n var pattern = /^([\\w-]*?)([\\d]+)$/;\n\n // sort morph target names into animation groups based\n // patterns like Walk_001, Walk_002, Run_001, Run_002\n for ( var i = 0, il = morphTargets.length; i < il; i ++ ) {\n\n var morphTarget = morphTargets[ i ];\n var parts = morphTarget.name.match( pattern );\n\n if ( parts && parts.length > 1 ) {\n\n var name = parts[ 1 ];\n\n var animationMorphTargets = animationToMorphTargets[ name ];\n if ( ! animationMorphTargets ) {\n\n animationToMorphTargets[ name ] = animationMorphTargets = [];\n\n }\n\n animationMorphTargets.push( morphTarget );\n\n }\n\n }\n\n var clips = [];\n\n for ( var name in animationToMorphTargets ) {\n\n clips.push( AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps, noLoop ) );\n\n }\n\n return clips;\n\n },\n\n // parse the animation.hierarchy format\n parseAnimation: function ( animation, bones ) {\n\n if ( ! animation ) {\n\n console.error( 'THREE.AnimationClip: No animation in JSONLoader data.' );\n return null;\n\n }\n\n var addNonemptyTrack = function ( trackType, trackName, animationKeys, propertyName, destTracks ) {\n\n // only return track if there are actually keys.\n if ( animationKeys.length !== 0 ) {\n\n var times = [];\n var values = [];\n\n AnimationUtils.flattenJSON( animationKeys, times, values, propertyName );\n\n // empty keys are filtered out, so check again\n if ( times.length !== 0 ) {\n\n destTracks.push( new trackType( trackName, times, values ) );\n\n }\n\n }\n\n };\n\n var tracks = [];\n\n var clipName = animation.name || 'default';\n // automatic length determination in AnimationClip.\n var duration = animation.length || - 1;\n var fps = animation.fps || 30;\n\n var hierarchyTracks = animation.hierarchy || [];\n\n for ( var h = 0; h < hierarchyTracks.length; h ++ ) {\n\n var animationKeys = hierarchyTracks[ h ].keys;\n\n // skip empty tracks\n if ( ! animationKeys || animationKeys.length === 0 ) continue;\n\n // process morph targets\n if ( animationKeys[ 0 ].morphTargets ) {\n\n // figure out all morph targets used in this track\n var morphTargetNames = {};\n\n for ( var k = 0; k < animationKeys.length; k ++ ) {\n\n if ( animationKeys[ k ].morphTargets ) {\n\n for ( var m = 0; m < animationKeys[ k ].morphTargets.length; m ++ ) {\n\n morphTargetNames[ animationKeys[ k ].morphTargets[ m ] ] = - 1;\n\n }\n\n }\n\n }\n\n // create a track for each morph target with all zero\n // morphTargetInfluences except for the keys in which\n // the morphTarget is named.\n for ( var morphTargetName in morphTargetNames ) {\n\n var times = [];\n var values = [];\n\n for ( var m = 0; m !== animationKeys[ k ].morphTargets.length; ++ m ) {\n\n var animationKey = animationKeys[ k ];\n\n times.push( animationKey.time );\n values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 );\n\n }\n\n tracks.push( new NumberKeyframeTrack( '.morphTargetInfluence[' + morphTargetName + ']', times, values ) );\n\n }\n\n duration = morphTargetNames.length * ( fps || 1.0 );\n\n } else {\n\n // ...assume skeletal animation\n\n var boneName = '.bones[' + bones[ h ].name + ']';\n\n addNonemptyTrack(\n VectorKeyframeTrack, boneName + '.position',\n animationKeys, 'pos', tracks );\n\n addNonemptyTrack(\n QuaternionKeyframeTrack, boneName + '.quaternion',\n animationKeys, 'rot', tracks );\n\n addNonemptyTrack(\n VectorKeyframeTrack, boneName + '.scale',\n animationKeys, 'scl', tracks );\n\n }\n\n }\n\n if ( tracks.length === 0 ) {\n\n return null;\n\n }\n\n var clip = new AnimationClip( clipName, duration, tracks );\n\n return clip;\n\n }\n\n } );\n\n Object.assign( AnimationClip.prototype, {\n\n resetDuration: function () {\n\n var tracks = this.tracks, duration = 0;\n\n for ( var i = 0, n = tracks.length; i !== n; ++ i ) {\n\n var track = this.tracks[ i ];\n\n duration = Math.max( duration, track.times[ track.times.length - 1 ] );\n\n }\n\n this.duration = duration;\n\n },\n\n trim: function () {\n\n for ( var i = 0; i < this.tracks.length; i ++ ) {\n\n this.tracks[ i ].trim( 0, this.duration );\n\n }\n\n return this;\n\n },\n\n optimize: function () {\n\n for ( var i = 0; i < this.tracks.length; i ++ ) {\n\n this.tracks[ i ].optimize();\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function MaterialLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n this.textures = {};\n\n }\n\n Object.assign( MaterialLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n onLoad( scope.parse( JSON.parse( text ) ) );\n\n }, onProgress, onError );\n\n },\n\n setTextures: function ( value ) {\n\n this.textures = value;\n\n },\n\n parse: function ( json ) {\n\n var textures = this.textures;\n\n function getTexture( name ) {\n\n if ( textures[ name ] === undefined ) {\n\n console.warn( 'THREE.MaterialLoader: Undefined texture', name );\n\n }\n\n return textures[ name ];\n\n }\n\n var material = new Materials[ json.type ]();\n\n if ( json.uuid !== undefined ) material.uuid = json.uuid;\n if ( json.name !== undefined ) material.name = json.name;\n if ( json.color !== undefined ) material.color.setHex( json.color );\n if ( json.roughness !== undefined ) material.roughness = json.roughness;\n if ( json.metalness !== undefined ) material.metalness = json.metalness;\n if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive );\n if ( json.specular !== undefined ) material.specular.setHex( json.specular );\n if ( json.shininess !== undefined ) material.shininess = json.shininess;\n if ( json.clearCoat !== undefined ) material.clearCoat = json.clearCoat;\n if ( json.clearCoatRoughness !== undefined ) material.clearCoatRoughness = json.clearCoatRoughness;\n if ( json.uniforms !== undefined ) material.uniforms = json.uniforms;\n if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader;\n if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader;\n if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors;\n if ( json.fog !== undefined ) material.fog = json.fog;\n if ( json.flatShading !== undefined ) material.flatShading = json.flatShading;\n if ( json.blending !== undefined ) material.blending = json.blending;\n if ( json.side !== undefined ) material.side = json.side;\n if ( json.opacity !== undefined ) material.opacity = json.opacity;\n if ( json.transparent !== undefined ) material.transparent = json.transparent;\n if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest;\n if ( json.depthTest !== undefined ) material.depthTest = json.depthTest;\n if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite;\n if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite;\n if ( json.wireframe !== undefined ) material.wireframe = json.wireframe;\n if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth;\n if ( json.wireframeLinecap !== undefined ) material.wireframeLinecap = json.wireframeLinecap;\n if ( json.wireframeLinejoin !== undefined ) material.wireframeLinejoin = json.wireframeLinejoin;\n\n if ( json.rotation !== undefined ) material.rotation = json.rotation;\n\n if ( json.linewidth !== 1 ) material.linewidth = json.linewidth;\n if ( json.dashSize !== undefined ) material.dashSize = json.dashSize;\n if ( json.gapSize !== undefined ) material.gapSize = json.gapSize;\n if ( json.scale !== undefined ) material.scale = json.scale;\n\n if ( json.polygonOffset !== undefined ) material.polygonOffset = json.polygonOffset;\n if ( json.polygonOffsetFactor !== undefined ) material.polygonOffsetFactor = json.polygonOffsetFactor;\n if ( json.polygonOffsetUnits !== undefined ) material.polygonOffsetUnits = json.polygonOffsetUnits;\n\n if ( json.skinning !== undefined ) material.skinning = json.skinning;\n if ( json.morphTargets !== undefined ) material.morphTargets = json.morphTargets;\n if ( json.dithering !== undefined ) material.dithering = json.dithering;\n\n if ( json.visible !== undefined ) material.visible = json.visible;\n if ( json.userData !== undefined ) material.userData = json.userData;\n\n // Deprecated\n\n if ( json.shading !== undefined ) material.flatShading = json.shading === 1; // THREE.FlatShading\n\n // for PointsMaterial\n\n if ( json.size !== undefined ) material.size = json.size;\n if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation;\n\n // maps\n\n if ( json.map !== undefined ) material.map = getTexture( json.map );\n\n if ( json.alphaMap !== undefined ) {\n\n material.alphaMap = getTexture( json.alphaMap );\n material.transparent = true;\n\n }\n\n if ( json.bumpMap !== undefined ) material.bumpMap = getTexture( json.bumpMap );\n if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale;\n\n if ( json.normalMap !== undefined ) material.normalMap = getTexture( json.normalMap );\n if ( json.normalScale !== undefined ) {\n\n var normalScale = json.normalScale;\n\n if ( Array.isArray( normalScale ) === false ) {\n\n // Blender exporter used to export a scalar. See #7459\n\n normalScale = [ normalScale, normalScale ];\n\n }\n\n material.normalScale = new Vector2().fromArray( normalScale );\n\n }\n\n if ( json.displacementMap !== undefined ) material.displacementMap = getTexture( json.displacementMap );\n if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale;\n if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias;\n\n if ( json.roughnessMap !== undefined ) material.roughnessMap = getTexture( json.roughnessMap );\n if ( json.metalnessMap !== undefined ) material.metalnessMap = getTexture( json.metalnessMap );\n\n if ( json.emissiveMap !== undefined ) material.emissiveMap = getTexture( json.emissiveMap );\n if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity;\n\n if ( json.specularMap !== undefined ) material.specularMap = getTexture( json.specularMap );\n\n if ( json.envMap !== undefined ) material.envMap = getTexture( json.envMap );\n\n if ( json.reflectivity !== undefined ) material.reflectivity = json.reflectivity;\n\n if ( json.lightMap !== undefined ) material.lightMap = getTexture( json.lightMap );\n if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity;\n\n if ( json.aoMap !== undefined ) material.aoMap = getTexture( json.aoMap );\n if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity;\n\n if ( json.gradientMap !== undefined ) material.gradientMap = getTexture( json.gradientMap );\n\n return material;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function BufferGeometryLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( BufferGeometryLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n onLoad( scope.parse( JSON.parse( text ) ) );\n\n }, onProgress, onError );\n\n },\n\n parse: function ( json ) {\n\n var geometry = new BufferGeometry();\n\n var index = json.data.index;\n\n if ( index !== undefined ) {\n\n var typedArray = new TYPED_ARRAYS[ index.type ]( index.array );\n geometry.setIndex( new BufferAttribute( typedArray, 1 ) );\n\n }\n\n var attributes = json.data.attributes;\n\n for ( var key in attributes ) {\n\n var attribute = attributes[ key ];\n var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array );\n\n geometry.addAttribute( key, new BufferAttribute( typedArray, attribute.itemSize, attribute.normalized ) );\n\n }\n\n var groups = json.data.groups || json.data.drawcalls || json.data.offsets;\n\n if ( groups !== undefined ) {\n\n for ( var i = 0, n = groups.length; i !== n; ++ i ) {\n\n var group = groups[ i ];\n\n geometry.addGroup( group.start, group.count, group.materialIndex );\n\n }\n\n }\n\n var boundingSphere = json.data.boundingSphere;\n\n if ( boundingSphere !== undefined ) {\n\n var center = new Vector3();\n\n if ( boundingSphere.center !== undefined ) {\n\n center.fromArray( boundingSphere.center );\n\n }\n\n geometry.boundingSphere = new Sphere( center, boundingSphere.radius );\n\n }\n\n return geometry;\n\n }\n\n } );\n\n var TYPED_ARRAYS = {\n Int8Array: Int8Array,\n Uint8Array: Uint8Array,\n // Workaround for IE11 pre KB2929437. See #11440\n Uint8ClampedArray: typeof Uint8ClampedArray !== 'undefined' ? Uint8ClampedArray : Uint8Array,\n Int16Array: Int16Array,\n Uint16Array: Uint16Array,\n Int32Array: Int32Array,\n Uint32Array: Uint32Array,\n Float32Array: Float32Array,\n Float64Array: Float64Array\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Loader() {}\n\n Loader.Handlers = {\n\n handlers: [],\n\n add: function ( regex, loader ) {\n\n this.handlers.push( regex, loader );\n\n },\n\n get: function ( file ) {\n\n var handlers = this.handlers;\n\n for ( var i = 0, l = handlers.length; i < l; i += 2 ) {\n\n var regex = handlers[ i ];\n var loader = handlers[ i + 1 ];\n\n if ( regex.test( file ) ) {\n\n return loader;\n\n }\n\n }\n\n return null;\n\n }\n\n };\n\n Object.assign( Loader.prototype, {\n\n crossOrigin: undefined,\n\n onLoadStart: function () {},\n\n onLoadProgress: function () {},\n\n onLoadComplete: function () {},\n\n initMaterials: function ( materials, texturePath, crossOrigin ) {\n\n var array = [];\n\n for ( var i = 0; i < materials.length; ++ i ) {\n\n array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin );\n\n }\n\n return array;\n\n },\n\n createMaterial: ( function () {\n\n var BlendingMode = {\n NoBlending: NoBlending,\n NormalBlending: NormalBlending,\n AdditiveBlending: AdditiveBlending,\n SubtractiveBlending: SubtractiveBlending,\n MultiplyBlending: MultiplyBlending,\n CustomBlending: CustomBlending\n };\n\n var color = new Color();\n var textureLoader = new TextureLoader();\n var materialLoader = new MaterialLoader();\n\n return function createMaterial( m, texturePath, crossOrigin ) {\n\n // convert from old material format\n\n var textures = {};\n\n function loadTexture( path, repeat, offset, wrap, anisotropy ) {\n\n var fullPath = texturePath + path;\n var loader = Loader.Handlers.get( fullPath );\n\n var texture;\n\n if ( loader !== null ) {\n\n texture = loader.load( fullPath );\n\n } else {\n\n textureLoader.setCrossOrigin( crossOrigin );\n texture = textureLoader.load( fullPath );\n\n }\n\n if ( repeat !== undefined ) {\n\n texture.repeat.fromArray( repeat );\n\n if ( repeat[ 0 ] !== 1 ) texture.wrapS = RepeatWrapping;\n if ( repeat[ 1 ] !== 1 ) texture.wrapT = RepeatWrapping;\n\n }\n\n if ( offset !== undefined ) {\n\n texture.offset.fromArray( offset );\n\n }\n\n if ( wrap !== undefined ) {\n\n if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = RepeatWrapping;\n if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = MirroredRepeatWrapping;\n\n if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = RepeatWrapping;\n if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = MirroredRepeatWrapping;\n\n }\n\n if ( anisotropy !== undefined ) {\n\n texture.anisotropy = anisotropy;\n\n }\n\n var uuid = _Math.generateUUID();\n\n textures[ uuid ] = texture;\n\n return uuid;\n\n }\n\n //\n\n var json = {\n uuid: _Math.generateUUID(),\n type: 'MeshLambertMaterial'\n };\n\n for ( var name in m ) {\n\n var value = m[ name ];\n\n switch ( name ) {\n\n case 'DbgColor':\n case 'DbgIndex':\n case 'opticalDensity':\n case 'illumination':\n break;\n case 'DbgName':\n json.name = value;\n break;\n case 'blending':\n json.blending = BlendingMode[ value ];\n break;\n case 'colorAmbient':\n case 'mapAmbient':\n console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' );\n break;\n case 'colorDiffuse':\n json.color = color.fromArray( value ).getHex();\n break;\n case 'colorSpecular':\n json.specular = color.fromArray( value ).getHex();\n break;\n case 'colorEmissive':\n json.emissive = color.fromArray( value ).getHex();\n break;\n case 'specularCoef':\n json.shininess = value;\n break;\n case 'shading':\n if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial';\n if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial';\n if ( value.toLowerCase() === 'standard' ) json.type = 'MeshStandardMaterial';\n break;\n case 'mapDiffuse':\n json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy );\n break;\n case 'mapDiffuseRepeat':\n case 'mapDiffuseOffset':\n case 'mapDiffuseWrap':\n case 'mapDiffuseAnisotropy':\n break;\n case 'mapEmissive':\n json.emissiveMap = loadTexture( value, m.mapEmissiveRepeat, m.mapEmissiveOffset, m.mapEmissiveWrap, m.mapEmissiveAnisotropy );\n break;\n case 'mapEmissiveRepeat':\n case 'mapEmissiveOffset':\n case 'mapEmissiveWrap':\n case 'mapEmissiveAnisotropy':\n break;\n case 'mapLight':\n json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy );\n break;\n case 'mapLightRepeat':\n case 'mapLightOffset':\n case 'mapLightWrap':\n case 'mapLightAnisotropy':\n break;\n case 'mapAO':\n json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy );\n break;\n case 'mapAORepeat':\n case 'mapAOOffset':\n case 'mapAOWrap':\n case 'mapAOAnisotropy':\n break;\n case 'mapBump':\n json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy );\n break;\n case 'mapBumpScale':\n json.bumpScale = value;\n break;\n case 'mapBumpRepeat':\n case 'mapBumpOffset':\n case 'mapBumpWrap':\n case 'mapBumpAnisotropy':\n break;\n case 'mapNormal':\n json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy );\n break;\n case 'mapNormalFactor':\n json.normalScale = value;\n break;\n case 'mapNormalRepeat':\n case 'mapNormalOffset':\n case 'mapNormalWrap':\n case 'mapNormalAnisotropy':\n break;\n case 'mapSpecular':\n json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy );\n break;\n case 'mapSpecularRepeat':\n case 'mapSpecularOffset':\n case 'mapSpecularWrap':\n case 'mapSpecularAnisotropy':\n break;\n case 'mapMetalness':\n json.metalnessMap = loadTexture( value, m.mapMetalnessRepeat, m.mapMetalnessOffset, m.mapMetalnessWrap, m.mapMetalnessAnisotropy );\n break;\n case 'mapMetalnessRepeat':\n case 'mapMetalnessOffset':\n case 'mapMetalnessWrap':\n case 'mapMetalnessAnisotropy':\n break;\n case 'mapRoughness':\n json.roughnessMap = loadTexture( value, m.mapRoughnessRepeat, m.mapRoughnessOffset, m.mapRoughnessWrap, m.mapRoughnessAnisotropy );\n break;\n case 'mapRoughnessRepeat':\n case 'mapRoughnessOffset':\n case 'mapRoughnessWrap':\n case 'mapRoughnessAnisotropy':\n break;\n case 'mapAlpha':\n json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy );\n break;\n case 'mapAlphaRepeat':\n case 'mapAlphaOffset':\n case 'mapAlphaWrap':\n case 'mapAlphaAnisotropy':\n break;\n case 'flipSided':\n json.side = BackSide;\n break;\n case 'doubleSided':\n json.side = DoubleSide;\n break;\n case 'transparency':\n console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' );\n json.opacity = value;\n break;\n case 'depthTest':\n case 'depthWrite':\n case 'colorWrite':\n case 'opacity':\n case 'reflectivity':\n case 'transparent':\n case 'visible':\n case 'wireframe':\n json[ name ] = value;\n break;\n case 'vertexColors':\n if ( value === true ) json.vertexColors = VertexColors;\n if ( value === 'face' ) json.vertexColors = FaceColors;\n break;\n default:\n console.error( 'THREE.Loader.createMaterial: Unsupported', name, value );\n break;\n\n }\n\n }\n\n if ( json.type === 'MeshBasicMaterial' ) delete json.emissive;\n if ( json.type !== 'MeshPhongMaterial' ) delete json.specular;\n\n if ( json.opacity < 1 ) json.transparent = true;\n\n materialLoader.setTextures( textures );\n\n return materialLoader.parse( json );\n\n };\n\n } )()\n\n } );\n\n /**\n * @author Don McCurdy / https://www.donmccurdy.com\n */\n\n var LoaderUtils = {\n\n decodeText: function ( array ) {\n\n if ( typeof TextDecoder !== 'undefined' ) {\n\n return new TextDecoder().decode( array );\n\n }\n\n // Avoid the String.fromCharCode.apply(null, array) shortcut, which\n // throws a \"maximum call stack size exceeded\" error for large arrays.\n\n var s = '';\n\n for ( var i = 0, il = array.length; i < il; i ++ ) {\n\n // Implicitly assumes little-endian.\n s += String.fromCharCode( array[ i ] );\n\n }\n\n // Merges multi-byte utf-8 characters.\n return decodeURIComponent( escape( s ) );\n\n },\n\n extractUrlBase: function ( url ) {\n\n var parts = url.split( '/' );\n\n if ( parts.length === 1 ) return './';\n\n parts.pop();\n\n return parts.join( '/' ) + '/';\n\n }\n\n };\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author alteredq / http://alteredqualia.com/\n */\n\n function JSONLoader( manager ) {\n\n if ( typeof manager === 'boolean' ) {\n\n console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' );\n manager = undefined;\n\n }\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n this.withCredentials = false;\n\n }\n\n Object.assign( JSONLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var texturePath = this.texturePath && ( typeof this.texturePath === 'string' ) ? this.texturePath : LoaderUtils.extractUrlBase( url );\n\n var loader = new FileLoader( this.manager );\n loader.setWithCredentials( this.withCredentials );\n loader.load( url, function ( text ) {\n\n var json = JSON.parse( text );\n var metadata = json.metadata;\n\n if ( metadata !== undefined ) {\n\n var type = metadata.type;\n\n if ( type !== undefined ) {\n\n if ( type.toLowerCase() === 'object' ) {\n\n console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' );\n return;\n\n }\n\n }\n\n }\n\n var object = scope.parse( json, texturePath );\n onLoad( object.geometry, object.materials );\n\n }, onProgress, onError );\n\n },\n\n setTexturePath: function ( value ) {\n\n this.texturePath = value;\n\n },\n\n parse: ( function () {\n\n function parseModel( json, geometry ) {\n\n function isBitSet( value, position ) {\n\n return value & ( 1 << position );\n\n }\n\n var i, j, fi,\n\n offset, zLength,\n\n colorIndex, normalIndex, uvIndex, materialIndex,\n\n type,\n isQuad,\n hasMaterial,\n hasFaceVertexUv,\n hasFaceNormal, hasFaceVertexNormal,\n hasFaceColor, hasFaceVertexColor,\n\n vertex, face, faceA, faceB, hex, normal,\n\n uvLayer, uv, u, v,\n\n faces = json.faces,\n vertices = json.vertices,\n normals = json.normals,\n colors = json.colors,\n\n scale = json.scale,\n\n nUvLayers = 0;\n\n\n if ( json.uvs !== undefined ) {\n\n // disregard empty arrays\n\n for ( i = 0; i < json.uvs.length; i ++ ) {\n\n if ( json.uvs[ i ].length ) nUvLayers ++;\n\n }\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n geometry.faceVertexUvs[ i ] = [];\n\n }\n\n }\n\n offset = 0;\n zLength = vertices.length;\n\n while ( offset < zLength ) {\n\n vertex = new Vector3();\n\n vertex.x = vertices[ offset ++ ] * scale;\n vertex.y = vertices[ offset ++ ] * scale;\n vertex.z = vertices[ offset ++ ] * scale;\n\n geometry.vertices.push( vertex );\n\n }\n\n offset = 0;\n zLength = faces.length;\n\n while ( offset < zLength ) {\n\n type = faces[ offset ++ ];\n\n isQuad = isBitSet( type, 0 );\n hasMaterial = isBitSet( type, 1 );\n hasFaceVertexUv = isBitSet( type, 3 );\n hasFaceNormal = isBitSet( type, 4 );\n hasFaceVertexNormal = isBitSet( type, 5 );\n hasFaceColor = isBitSet( type, 6 );\n hasFaceVertexColor = isBitSet( type, 7 );\n\n // console.log(\"type\", type, \"bits\", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor);\n\n if ( isQuad ) {\n\n faceA = new Face3();\n faceA.a = faces[ offset ];\n faceA.b = faces[ offset + 1 ];\n faceA.c = faces[ offset + 3 ];\n\n faceB = new Face3();\n faceB.a = faces[ offset + 1 ];\n faceB.b = faces[ offset + 2 ];\n faceB.c = faces[ offset + 3 ];\n\n offset += 4;\n\n if ( hasMaterial ) {\n\n materialIndex = faces[ offset ++ ];\n faceA.materialIndex = materialIndex;\n faceB.materialIndex = materialIndex;\n\n }\n\n // to get face <=> uv index correspondence\n\n fi = geometry.faces.length;\n\n if ( hasFaceVertexUv ) {\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n uvLayer = json.uvs[ i ];\n\n geometry.faceVertexUvs[ i ][ fi ] = [];\n geometry.faceVertexUvs[ i ][ fi + 1 ] = [];\n\n for ( j = 0; j < 4; j ++ ) {\n\n uvIndex = faces[ offset ++ ];\n\n u = uvLayer[ uvIndex * 2 ];\n v = uvLayer[ uvIndex * 2 + 1 ];\n\n uv = new Vector2( u, v );\n\n if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv );\n if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv );\n\n }\n\n }\n\n }\n\n if ( hasFaceNormal ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n faceA.normal.set(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n faceB.normal.copy( faceA.normal );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n for ( i = 0; i < 4; i ++ ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n normal = new Vector3(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n\n if ( i !== 2 ) faceA.vertexNormals.push( normal );\n if ( i !== 0 ) faceB.vertexNormals.push( normal );\n\n }\n\n }\n\n\n if ( hasFaceColor ) {\n\n colorIndex = faces[ offset ++ ];\n hex = colors[ colorIndex ];\n\n faceA.color.setHex( hex );\n faceB.color.setHex( hex );\n\n }\n\n\n if ( hasFaceVertexColor ) {\n\n for ( i = 0; i < 4; i ++ ) {\n\n colorIndex = faces[ offset ++ ];\n hex = colors[ colorIndex ];\n\n if ( i !== 2 ) faceA.vertexColors.push( new Color( hex ) );\n if ( i !== 0 ) faceB.vertexColors.push( new Color( hex ) );\n\n }\n\n }\n\n geometry.faces.push( faceA );\n geometry.faces.push( faceB );\n\n } else {\n\n face = new Face3();\n face.a = faces[ offset ++ ];\n face.b = faces[ offset ++ ];\n face.c = faces[ offset ++ ];\n\n if ( hasMaterial ) {\n\n materialIndex = faces[ offset ++ ];\n face.materialIndex = materialIndex;\n\n }\n\n // to get face <=> uv index correspondence\n\n fi = geometry.faces.length;\n\n if ( hasFaceVertexUv ) {\n\n for ( i = 0; i < nUvLayers; i ++ ) {\n\n uvLayer = json.uvs[ i ];\n\n geometry.faceVertexUvs[ i ][ fi ] = [];\n\n for ( j = 0; j < 3; j ++ ) {\n\n uvIndex = faces[ offset ++ ];\n\n u = uvLayer[ uvIndex * 2 ];\n v = uvLayer[ uvIndex * 2 + 1 ];\n\n uv = new Vector2( u, v );\n\n geometry.faceVertexUvs[ i ][ fi ].push( uv );\n\n }\n\n }\n\n }\n\n if ( hasFaceNormal ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n face.normal.set(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n }\n\n if ( hasFaceVertexNormal ) {\n\n for ( i = 0; i < 3; i ++ ) {\n\n normalIndex = faces[ offset ++ ] * 3;\n\n normal = new Vector3(\n normals[ normalIndex ++ ],\n normals[ normalIndex ++ ],\n normals[ normalIndex ]\n );\n\n face.vertexNormals.push( normal );\n\n }\n\n }\n\n\n if ( hasFaceColor ) {\n\n colorIndex = faces[ offset ++ ];\n face.color.setHex( colors[ colorIndex ] );\n\n }\n\n\n if ( hasFaceVertexColor ) {\n\n for ( i = 0; i < 3; i ++ ) {\n\n colorIndex = faces[ offset ++ ];\n face.vertexColors.push( new Color( colors[ colorIndex ] ) );\n\n }\n\n }\n\n geometry.faces.push( face );\n\n }\n\n }\n\n }\n\n function parseSkin( json, geometry ) {\n\n var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2;\n\n if ( json.skinWeights ) {\n\n for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) {\n\n var x = json.skinWeights[ i ];\n var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0;\n var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0;\n var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0;\n\n geometry.skinWeights.push( new Vector4( x, y, z, w ) );\n\n }\n\n }\n\n if ( json.skinIndices ) {\n\n for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) {\n\n var a = json.skinIndices[ i ];\n var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0;\n var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0;\n var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0;\n\n geometry.skinIndices.push( new Vector4( a, b, c, d ) );\n\n }\n\n }\n\n geometry.bones = json.bones;\n\n if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) {\n\n console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' +\n geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' );\n\n }\n\n }\n\n function parseMorphing( json, geometry ) {\n\n var scale = json.scale;\n\n if ( json.morphTargets !== undefined ) {\n\n for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) {\n\n geometry.morphTargets[ i ] = {};\n geometry.morphTargets[ i ].name = json.morphTargets[ i ].name;\n geometry.morphTargets[ i ].vertices = [];\n\n var dstVertices = geometry.morphTargets[ i ].vertices;\n var srcVertices = json.morphTargets[ i ].vertices;\n\n for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) {\n\n var vertex = new Vector3();\n vertex.x = srcVertices[ v ] * scale;\n vertex.y = srcVertices[ v + 1 ] * scale;\n vertex.z = srcVertices[ v + 2 ] * scale;\n\n dstVertices.push( vertex );\n\n }\n\n }\n\n }\n\n if ( json.morphColors !== undefined && json.morphColors.length > 0 ) {\n\n console.warn( 'THREE.JSONLoader: \"morphColors\" no longer supported. Using them as face colors.' );\n\n var faces = geometry.faces;\n var morphColors = json.morphColors[ 0 ].colors;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n faces[ i ].color.fromArray( morphColors, i * 3 );\n\n }\n\n }\n\n }\n\n function parseAnimations( json, geometry ) {\n\n var outputAnimations = [];\n\n // parse old style Bone/Hierarchy animations\n var animations = [];\n\n if ( json.animation !== undefined ) {\n\n animations.push( json.animation );\n\n }\n\n if ( json.animations !== undefined ) {\n\n if ( json.animations.length ) {\n\n animations = animations.concat( json.animations );\n\n } else {\n\n animations.push( json.animations );\n\n }\n\n }\n\n for ( var i = 0; i < animations.length; i ++ ) {\n\n var clip = AnimationClip.parseAnimation( animations[ i ], geometry.bones );\n if ( clip ) outputAnimations.push( clip );\n\n }\n\n // parse implicit morph animations\n if ( geometry.morphTargets ) {\n\n // TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary.\n var morphAnimationClips = AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 );\n outputAnimations = outputAnimations.concat( morphAnimationClips );\n\n }\n\n if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations;\n\n }\n\n return function parse( json, texturePath ) {\n\n if ( json.data !== undefined ) {\n\n // Geometry 4.0 spec\n json = json.data;\n\n }\n\n if ( json.scale !== undefined ) {\n\n json.scale = 1.0 / json.scale;\n\n } else {\n\n json.scale = 1.0;\n\n }\n\n var geometry = new Geometry();\n\n parseModel( json, geometry );\n parseSkin( json, geometry );\n parseMorphing( json, geometry );\n parseAnimations( json, geometry );\n\n geometry.computeFaceNormals();\n geometry.computeBoundingSphere();\n\n if ( json.materials === undefined || json.materials.length === 0 ) {\n\n return { geometry: geometry };\n\n } else {\n\n var materials = Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin );\n\n return { geometry: geometry, materials: materials };\n\n }\n\n };\n\n } )()\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function ObjectLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n this.texturePath = '';\n\n }\n\n Object.assign( ObjectLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n if ( this.texturePath === '' ) {\n\n this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 );\n\n }\n\n var scope = this;\n\n var loader = new FileLoader( scope.manager );\n loader.load( url, function ( text ) {\n\n var json = null;\n\n try {\n\n json = JSON.parse( text );\n\n } catch ( error ) {\n\n if ( onError !== undefined ) onError( error );\n\n console.error( 'THREE:ObjectLoader: Can\\'t parse ' + url + '.', error.message );\n\n return;\n\n }\n\n var metadata = json.metadata;\n\n if ( metadata === undefined || metadata.type === undefined || metadata.type.toLowerCase() === 'geometry' ) {\n\n console.error( 'THREE.ObjectLoader: Can\\'t load ' + url + '. Use THREE.JSONLoader instead.' );\n return;\n\n }\n\n scope.parse( json, onLoad );\n\n }, onProgress, onError );\n\n },\n\n setTexturePath: function ( value ) {\n\n this.texturePath = value;\n\n },\n\n setCrossOrigin: function ( value ) {\n\n this.crossOrigin = value;\n\n },\n\n parse: function ( json, onLoad ) {\n\n var shapes = this.parseShape( json.shapes );\n var geometries = this.parseGeometries( json.geometries, shapes );\n\n var images = this.parseImages( json.images, function () {\n\n if ( onLoad !== undefined ) onLoad( object );\n\n } );\n\n var textures = this.parseTextures( json.textures, images );\n var materials = this.parseMaterials( json.materials, textures );\n\n var object = this.parseObject( json.object, geometries, materials );\n\n if ( json.animations ) {\n\n object.animations = this.parseAnimations( json.animations );\n\n }\n\n if ( json.images === undefined || json.images.length === 0 ) {\n\n if ( onLoad !== undefined ) onLoad( object );\n\n }\n\n return object;\n\n },\n\n parseShape: function ( json ) {\n\n var shapes = {};\n\n if ( json !== undefined ) {\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var shape = new Shape().fromJSON( json[ i ] );\n\n shapes[ shape.uuid ] = shape;\n\n }\n\n }\n\n return shapes;\n\n },\n\n parseGeometries: function ( json, shapes ) {\n\n var geometries = {};\n\n if ( json !== undefined ) {\n\n var geometryLoader = new JSONLoader();\n var bufferGeometryLoader = new BufferGeometryLoader();\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var geometry;\n var data = json[ i ];\n\n switch ( data.type ) {\n\n case 'PlaneGeometry':\n case 'PlaneBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.width,\n data.height,\n data.widthSegments,\n data.heightSegments\n );\n\n break;\n\n case 'BoxGeometry':\n case 'BoxBufferGeometry':\n case 'CubeGeometry': // backwards compatible\n\n geometry = new Geometries[ data.type ](\n data.width,\n data.height,\n data.depth,\n data.widthSegments,\n data.heightSegments,\n data.depthSegments\n );\n\n break;\n\n case 'CircleGeometry':\n case 'CircleBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.segments,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'CylinderGeometry':\n case 'CylinderBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radiusTop,\n data.radiusBottom,\n data.height,\n data.radialSegments,\n data.heightSegments,\n data.openEnded,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'ConeGeometry':\n case 'ConeBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.height,\n data.radialSegments,\n data.heightSegments,\n data.openEnded,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'SphereGeometry':\n case 'SphereBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.widthSegments,\n data.heightSegments,\n data.phiStart,\n data.phiLength,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'DodecahedronGeometry':\n case 'DodecahedronBufferGeometry':\n case 'IcosahedronGeometry':\n case 'IcosahedronBufferGeometry':\n case 'OctahedronGeometry':\n case 'OctahedronBufferGeometry':\n case 'TetrahedronGeometry':\n case 'TetrahedronBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.detail\n );\n\n break;\n\n case 'RingGeometry':\n case 'RingBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.innerRadius,\n data.outerRadius,\n data.thetaSegments,\n data.phiSegments,\n data.thetaStart,\n data.thetaLength\n );\n\n break;\n\n case 'TorusGeometry':\n case 'TorusBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.tube,\n data.radialSegments,\n data.tubularSegments,\n data.arc\n );\n\n break;\n\n case 'TorusKnotGeometry':\n case 'TorusKnotBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.radius,\n data.tube,\n data.tubularSegments,\n data.radialSegments,\n data.p,\n data.q\n );\n\n break;\n\n case 'LatheGeometry':\n case 'LatheBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.points,\n data.segments,\n data.phiStart,\n data.phiLength\n );\n\n break;\n\n case 'PolyhedronGeometry':\n case 'PolyhedronBufferGeometry':\n\n geometry = new Geometries[ data.type ](\n data.vertices,\n data.indices,\n data.radius,\n data.details\n );\n\n break;\n\n case 'ShapeGeometry':\n case 'ShapeBufferGeometry':\n\n var geometryShapes = [];\n\n for ( var i = 0, l = data.shapes.length; i < l; i ++ ) {\n\n var shape = shapes[ data.shapes[ i ] ];\n\n geometryShapes.push( shape );\n\n }\n\n geometry = new Geometries[ data.type ](\n geometryShapes,\n data.curveSegments\n );\n\n break;\n\n case 'BufferGeometry':\n\n geometry = bufferGeometryLoader.parse( data );\n\n break;\n\n case 'Geometry':\n\n geometry = geometryLoader.parse( data, this.texturePath ).geometry;\n\n break;\n\n default:\n\n console.warn( 'THREE.ObjectLoader: Unsupported geometry type \"' + data.type + '\"' );\n\n continue;\n\n }\n\n geometry.uuid = data.uuid;\n\n if ( data.name !== undefined ) geometry.name = data.name;\n\n geometries[ data.uuid ] = geometry;\n\n }\n\n }\n\n return geometries;\n\n },\n\n parseMaterials: function ( json, textures ) {\n\n var materials = {};\n\n if ( json !== undefined ) {\n\n var loader = new MaterialLoader();\n loader.setTextures( textures );\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var data = json[ i ];\n\n if ( data.type === 'MultiMaterial' ) {\n\n // Deprecated\n\n var array = [];\n\n for ( var j = 0; j < data.materials.length; j ++ ) {\n\n array.push( loader.parse( data.materials[ j ] ) );\n\n }\n\n materials[ data.uuid ] = array;\n\n } else {\n\n materials[ data.uuid ] = loader.parse( data );\n\n }\n\n }\n\n }\n\n return materials;\n\n },\n\n parseAnimations: function ( json ) {\n\n var animations = [];\n\n for ( var i = 0; i < json.length; i ++ ) {\n\n var clip = AnimationClip.parse( json[ i ] );\n\n animations.push( clip );\n\n }\n\n return animations;\n\n },\n\n parseImages: function ( json, onLoad ) {\n\n var scope = this;\n var images = {};\n\n function loadImage( url ) {\n\n scope.manager.itemStart( url );\n\n return loader.load( url, function () {\n\n scope.manager.itemEnd( url );\n\n }, undefined, function () {\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n } );\n\n }\n\n if ( json !== undefined && json.length > 0 ) {\n\n var manager = new LoadingManager( onLoad );\n\n var loader = new ImageLoader( manager );\n loader.setCrossOrigin( this.crossOrigin );\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var image = json[ i ];\n var path = /^(\\/\\/)|([a-z]+:(\\/\\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url;\n\n images[ image.uuid ] = loadImage( path );\n\n }\n\n }\n\n return images;\n\n },\n\n parseTextures: function ( json, images ) {\n\n function parseConstant( value, type ) {\n\n if ( typeof value === 'number' ) return value;\n\n console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value );\n\n return type[ value ];\n\n }\n\n var textures = {};\n\n if ( json !== undefined ) {\n\n for ( var i = 0, l = json.length; i < l; i ++ ) {\n\n var data = json[ i ];\n\n if ( data.image === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: No \"image\" specified for', data.uuid );\n\n }\n\n if ( images[ data.image ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined image', data.image );\n\n }\n\n var texture = new Texture( images[ data.image ] );\n texture.needsUpdate = true;\n\n texture.uuid = data.uuid;\n\n if ( data.name !== undefined ) texture.name = data.name;\n\n if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping, TEXTURE_MAPPING );\n\n if ( data.offset !== undefined ) texture.offset.fromArray( data.offset );\n if ( data.repeat !== undefined ) texture.repeat.fromArray( data.repeat );\n if ( data.center !== undefined ) texture.center.fromArray( data.center );\n if ( data.rotation !== undefined ) texture.rotation = data.rotation;\n\n if ( data.wrap !== undefined ) {\n\n texture.wrapS = parseConstant( data.wrap[ 0 ], TEXTURE_WRAPPING );\n texture.wrapT = parseConstant( data.wrap[ 1 ], TEXTURE_WRAPPING );\n\n }\n\n if ( data.format !== undefined ) texture.format = data.format;\n\n if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter, TEXTURE_FILTER );\n if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter, TEXTURE_FILTER );\n if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy;\n\n if ( data.flipY !== undefined ) texture.flipY = data.flipY;\n\n textures[ data.uuid ] = texture;\n\n }\n\n }\n\n return textures;\n\n },\n\n parseObject: function ( data, geometries, materials ) {\n\n var object;\n\n function getGeometry( name ) {\n\n if ( geometries[ name ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined geometry', name );\n\n }\n\n return geometries[ name ];\n\n }\n\n function getMaterial( name ) {\n\n if ( name === undefined ) return undefined;\n\n if ( Array.isArray( name ) ) {\n\n var array = [];\n\n for ( var i = 0, l = name.length; i < l; i ++ ) {\n\n var uuid = name[ i ];\n\n if ( materials[ uuid ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined material', uuid );\n\n }\n\n array.push( materials[ uuid ] );\n\n }\n\n return array;\n\n }\n\n if ( materials[ name ] === undefined ) {\n\n console.warn( 'THREE.ObjectLoader: Undefined material', name );\n\n }\n\n return materials[ name ];\n\n }\n\n switch ( data.type ) {\n\n case 'Scene':\n\n object = new Scene();\n\n if ( data.background !== undefined ) {\n\n if ( Number.isInteger( data.background ) ) {\n\n object.background = new Color( data.background );\n\n }\n\n }\n\n if ( data.fog !== undefined ) {\n\n if ( data.fog.type === 'Fog' ) {\n\n object.fog = new Fog( data.fog.color, data.fog.near, data.fog.far );\n\n } else if ( data.fog.type === 'FogExp2' ) {\n\n object.fog = new FogExp2( data.fog.color, data.fog.density );\n\n }\n\n }\n\n break;\n\n case 'PerspectiveCamera':\n\n object = new PerspectiveCamera( data.fov, data.aspect, data.near, data.far );\n\n if ( data.focus !== undefined ) object.focus = data.focus;\n if ( data.zoom !== undefined ) object.zoom = data.zoom;\n if ( data.filmGauge !== undefined ) object.filmGauge = data.filmGauge;\n if ( data.filmOffset !== undefined ) object.filmOffset = data.filmOffset;\n if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n break;\n\n case 'OrthographicCamera':\n\n object = new OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far );\n\n if ( data.zoom !== undefined ) object.zoom = data.zoom;\n if ( data.view !== undefined ) object.view = Object.assign( {}, data.view );\n\n break;\n\n case 'AmbientLight':\n\n object = new AmbientLight( data.color, data.intensity );\n\n break;\n\n case 'DirectionalLight':\n\n object = new DirectionalLight( data.color, data.intensity );\n\n break;\n\n case 'PointLight':\n\n object = new PointLight( data.color, data.intensity, data.distance, data.decay );\n\n break;\n\n case 'RectAreaLight':\n\n object = new RectAreaLight( data.color, data.intensity, data.width, data.height );\n\n break;\n\n case 'SpotLight':\n\n object = new SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay );\n\n break;\n\n case 'HemisphereLight':\n\n object = new HemisphereLight( data.color, data.groundColor, data.intensity );\n\n break;\n\n case 'SkinnedMesh':\n\n console.warn( 'THREE.ObjectLoader.parseObject() does not support SkinnedMesh yet.' );\n\n case 'Mesh':\n\n var geometry = getGeometry( data.geometry );\n var material = getMaterial( data.material );\n\n if ( geometry.bones && geometry.bones.length > 0 ) {\n\n object = new SkinnedMesh( geometry, material );\n\n } else {\n\n object = new Mesh( geometry, material );\n\n }\n\n break;\n\n case 'LOD':\n\n object = new LOD();\n\n break;\n\n case 'Line':\n\n object = new Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode );\n\n break;\n\n case 'LineLoop':\n\n object = new LineLoop( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'LineSegments':\n\n object = new LineSegments( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'PointCloud':\n case 'Points':\n\n object = new Points( getGeometry( data.geometry ), getMaterial( data.material ) );\n\n break;\n\n case 'Sprite':\n\n object = new Sprite( getMaterial( data.material ) );\n\n break;\n\n case 'Group':\n\n object = new Group();\n\n break;\n\n default:\n\n object = new Object3D();\n\n }\n\n object.uuid = data.uuid;\n\n if ( data.name !== undefined ) object.name = data.name;\n if ( data.matrix !== undefined ) {\n\n object.matrix.fromArray( data.matrix );\n object.matrix.decompose( object.position, object.quaternion, object.scale );\n\n } else {\n\n if ( data.position !== undefined ) object.position.fromArray( data.position );\n if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation );\n if ( data.quaternion !== undefined ) object.quaternion.fromArray( data.quaternion );\n if ( data.scale !== undefined ) object.scale.fromArray( data.scale );\n\n }\n\n if ( data.castShadow !== undefined ) object.castShadow = data.castShadow;\n if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow;\n\n if ( data.shadow ) {\n\n if ( data.shadow.bias !== undefined ) object.shadow.bias = data.shadow.bias;\n if ( data.shadow.radius !== undefined ) object.shadow.radius = data.shadow.radius;\n if ( data.shadow.mapSize !== undefined ) object.shadow.mapSize.fromArray( data.shadow.mapSize );\n if ( data.shadow.camera !== undefined ) object.shadow.camera = this.parseObject( data.shadow.camera );\n\n }\n\n if ( data.visible !== undefined ) object.visible = data.visible;\n if ( data.frustumCulled !== undefined ) object.frustumCulled = data.frustumCulled;\n if ( data.renderOrder !== undefined ) object.renderOrder = data.renderOrder;\n if ( data.userData !== undefined ) object.userData = data.userData;\n\n if ( data.children !== undefined ) {\n\n var children = data.children;\n\n for ( var i = 0; i < children.length; i ++ ) {\n\n object.add( this.parseObject( children[ i ], geometries, materials ) );\n\n }\n\n }\n\n if ( data.type === 'LOD' ) {\n\n var levels = data.levels;\n\n for ( var l = 0; l < levels.length; l ++ ) {\n\n var level = levels[ l ];\n var child = object.getObjectByProperty( 'uuid', level.object );\n\n if ( child !== undefined ) {\n\n object.addLevel( child, level.distance );\n\n }\n\n }\n\n }\n\n return object;\n\n }\n\n } );\n\n var TEXTURE_MAPPING = {\n UVMapping: UVMapping,\n CubeReflectionMapping: CubeReflectionMapping,\n CubeRefractionMapping: CubeRefractionMapping,\n EquirectangularReflectionMapping: EquirectangularReflectionMapping,\n EquirectangularRefractionMapping: EquirectangularRefractionMapping,\n SphericalReflectionMapping: SphericalReflectionMapping,\n CubeUVReflectionMapping: CubeUVReflectionMapping,\n CubeUVRefractionMapping: CubeUVRefractionMapping\n };\n\n var TEXTURE_WRAPPING = {\n RepeatWrapping: RepeatWrapping,\n ClampToEdgeWrapping: ClampToEdgeWrapping,\n MirroredRepeatWrapping: MirroredRepeatWrapping\n };\n\n var TEXTURE_FILTER = {\n NearestFilter: NearestFilter,\n NearestMipMapNearestFilter: NearestMipMapNearestFilter,\n NearestMipMapLinearFilter: NearestMipMapLinearFilter,\n LinearFilter: LinearFilter,\n LinearMipMapNearestFilter: LinearMipMapNearestFilter,\n LinearMipMapLinearFilter: LinearMipMapLinearFilter\n };\n\n /**\n * @author thespite / http://clicktorelease.com/\n */\n\n function ImageBitmapLoader( manager ) {\n\n if ( typeof createImageBitmap === 'undefined' ) {\n\n console.warn( 'THREE.ImageBitmapLoader: createImageBitmap() not supported.' );\n\n }\n\n if ( typeof fetch === 'undefined' ) {\n\n console.warn( 'THREE.ImageBitmapLoader: fetch() not supported.' );\n\n }\n\n this.manager = manager !== undefined ? manager : DefaultLoadingManager;\n this.options = undefined;\n\n }\n\n ImageBitmapLoader.prototype = {\n\n constructor: ImageBitmapLoader,\n\n setOptions: function setOptions( options ) {\n\n this.options = options;\n\n return this;\n\n },\n\n load: function load( url, onLoad, onProgress, onError ) {\n\n if ( url === undefined ) url = '';\n\n if ( this.path !== undefined ) url = this.path + url;\n\n var scope = this;\n\n var cached = Cache.get( url );\n\n if ( cached !== undefined ) {\n\n scope.manager.itemStart( url );\n\n setTimeout( function () {\n\n if ( onLoad ) onLoad( cached );\n\n scope.manager.itemEnd( url );\n\n }, 0 );\n\n return cached;\n\n }\n\n fetch( url ).then( function ( res ) {\n\n return res.blob();\n\n } ).then( function ( blob ) {\n\n return createImageBitmap( blob, scope.options );\n\n } ).then( function ( imageBitmap ) {\n\n Cache.add( url, imageBitmap );\n\n if ( onLoad ) onLoad( imageBitmap );\n\n scope.manager.itemEnd( url );\n\n } ).catch( function ( e ) {\n\n if ( onError ) onError( e );\n\n scope.manager.itemEnd( url );\n scope.manager.itemError( url );\n\n } );\n\n },\n\n setCrossOrigin: function ( /* value */ ) {\n\n return this;\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n };\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * minimal class for proxing functions to Path. Replaces old \"extractSubpaths()\"\n **/\n\n function ShapePath() {\n\n this.type = 'ShapePath';\n\n this.subPaths = [];\n this.currentPath = null;\n\n }\n\n Object.assign( ShapePath.prototype, {\n\n moveTo: function ( x, y ) {\n\n this.currentPath = new Path();\n this.subPaths.push( this.currentPath );\n this.currentPath.moveTo( x, y );\n\n },\n\n lineTo: function ( x, y ) {\n\n this.currentPath.lineTo( x, y );\n\n },\n\n quadraticCurveTo: function ( aCPx, aCPy, aX, aY ) {\n\n this.currentPath.quadraticCurveTo( aCPx, aCPy, aX, aY );\n\n },\n\n bezierCurveTo: function ( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY ) {\n\n this.currentPath.bezierCurveTo( aCP1x, aCP1y, aCP2x, aCP2y, aX, aY );\n\n },\n\n splineThru: function ( pts ) {\n\n this.currentPath.splineThru( pts );\n\n },\n\n toShapes: function ( isCCW, noHoles ) {\n\n function toShapesNoHoles( inSubpaths ) {\n\n var shapes = [];\n\n for ( var i = 0, l = inSubpaths.length; i < l; i ++ ) {\n\n var tmpPath = inSubpaths[ i ];\n\n var tmpShape = new Shape();\n tmpShape.curves = tmpPath.curves;\n\n shapes.push( tmpShape );\n\n }\n\n return shapes;\n\n }\n\n function isPointInsidePolygon( inPt, inPolygon ) {\n\n var polyLen = inPolygon.length;\n\n // inPt on polygon contour => immediate success or\n // toggling of inside/outside at every single! intersection point of an edge\n // with the horizontal line through inPt, left of inPt\n // not counting lowerY endpoints of edges and whole edges on that line\n var inside = false;\n for ( var p = polyLen - 1, q = 0; q < polyLen; p = q ++ ) {\n\n var edgeLowPt = inPolygon[ p ];\n var edgeHighPt = inPolygon[ q ];\n\n var edgeDx = edgeHighPt.x - edgeLowPt.x;\n var edgeDy = edgeHighPt.y - edgeLowPt.y;\n\n if ( Math.abs( edgeDy ) > Number.EPSILON ) {\n\n // not parallel\n if ( edgeDy < 0 ) {\n\n edgeLowPt = inPolygon[ q ]; edgeDx = - edgeDx;\n edgeHighPt = inPolygon[ p ]; edgeDy = - edgeDy;\n\n }\n if ( ( inPt.y < edgeLowPt.y ) || ( inPt.y > edgeHighPt.y ) ) continue;\n\n if ( inPt.y === edgeLowPt.y ) {\n\n if ( inPt.x === edgeLowPt.x ) return true; // inPt is on contour ?\n // continue; // no intersection or edgeLowPt => doesn't count !!!\n\n } else {\n\n var perpEdge = edgeDy * ( inPt.x - edgeLowPt.x ) - edgeDx * ( inPt.y - edgeLowPt.y );\n if ( perpEdge === 0 ) return true; // inPt is on contour ?\n if ( perpEdge < 0 ) continue;\n inside = ! inside; // true intersection left of inPt\n\n }\n\n } else {\n\n // parallel or collinear\n if ( inPt.y !== edgeLowPt.y ) continue; // parallel\n // edge lies on the same horizontal line as inPt\n if ( ( ( edgeHighPt.x <= inPt.x ) && ( inPt.x <= edgeLowPt.x ) ) ||\n ( ( edgeLowPt.x <= inPt.x ) && ( inPt.x <= edgeHighPt.x ) ) ) return true; // inPt: Point on contour !\n // continue;\n\n }\n\n }\n\n return inside;\n\n }\n\n var isClockWise = ShapeUtils.isClockWise;\n\n var subPaths = this.subPaths;\n if ( subPaths.length === 0 ) return [];\n\n if ( noHoles === true ) return toShapesNoHoles( subPaths );\n\n\n var solid, tmpPath, tmpShape, shapes = [];\n\n if ( subPaths.length === 1 ) {\n\n tmpPath = subPaths[ 0 ];\n tmpShape = new Shape();\n tmpShape.curves = tmpPath.curves;\n shapes.push( tmpShape );\n return shapes;\n\n }\n\n var holesFirst = ! isClockWise( subPaths[ 0 ].getPoints() );\n holesFirst = isCCW ? ! holesFirst : holesFirst;\n\n // console.log(\"Holes first\", holesFirst);\n\n var betterShapeHoles = [];\n var newShapes = [];\n var newShapeHoles = [];\n var mainIdx = 0;\n var tmpPoints;\n\n newShapes[ mainIdx ] = undefined;\n newShapeHoles[ mainIdx ] = [];\n\n for ( var i = 0, l = subPaths.length; i < l; i ++ ) {\n\n tmpPath = subPaths[ i ];\n tmpPoints = tmpPath.getPoints();\n solid = isClockWise( tmpPoints );\n solid = isCCW ? ! solid : solid;\n\n if ( solid ) {\n\n if ( ( ! holesFirst ) && ( newShapes[ mainIdx ] ) ) mainIdx ++;\n\n newShapes[ mainIdx ] = { s: new Shape(), p: tmpPoints };\n newShapes[ mainIdx ].s.curves = tmpPath.curves;\n\n if ( holesFirst ) mainIdx ++;\n newShapeHoles[ mainIdx ] = [];\n\n //console.log('cw', i);\n\n } else {\n\n newShapeHoles[ mainIdx ].push( { h: tmpPath, p: tmpPoints[ 0 ] } );\n\n //console.log('ccw', i);\n\n }\n\n }\n\n // only Holes? -> probably all Shapes with wrong orientation\n if ( ! newShapes[ 0 ] ) return toShapesNoHoles( subPaths );\n\n\n if ( newShapes.length > 1 ) {\n\n var ambiguous = false;\n var toChange = [];\n\n for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n betterShapeHoles[ sIdx ] = [];\n\n }\n\n for ( var sIdx = 0, sLen = newShapes.length; sIdx < sLen; sIdx ++ ) {\n\n var sho = newShapeHoles[ sIdx ];\n\n for ( var hIdx = 0; hIdx < sho.length; hIdx ++ ) {\n\n var ho = sho[ hIdx ];\n var hole_unassigned = true;\n\n for ( var s2Idx = 0; s2Idx < newShapes.length; s2Idx ++ ) {\n\n if ( isPointInsidePolygon( ho.p, newShapes[ s2Idx ].p ) ) {\n\n if ( sIdx !== s2Idx ) toChange.push( { froms: sIdx, tos: s2Idx, hole: hIdx } );\n if ( hole_unassigned ) {\n\n hole_unassigned = false;\n betterShapeHoles[ s2Idx ].push( ho );\n\n } else {\n\n ambiguous = true;\n\n }\n\n }\n\n }\n if ( hole_unassigned ) {\n\n betterShapeHoles[ sIdx ].push( ho );\n\n }\n\n }\n\n }\n // console.log(\"ambiguous: \", ambiguous);\n if ( toChange.length > 0 ) {\n\n // console.log(\"to change: \", toChange);\n if ( ! ambiguous ) newShapeHoles = betterShapeHoles;\n\n }\n\n }\n\n var tmpHoles;\n\n for ( var i = 0, il = newShapes.length; i < il; i ++ ) {\n\n tmpShape = newShapes[ i ].s;\n shapes.push( tmpShape );\n tmpHoles = newShapeHoles[ i ];\n\n for ( var j = 0, jl = tmpHoles.length; j < jl; j ++ ) {\n\n tmpShape.holes.push( tmpHoles[ j ].h );\n\n }\n\n }\n\n //console.log(\"shape\", shapes);\n\n return shapes;\n\n }\n\n } );\n\n /**\n * @author zz85 / http://www.lab4games.net/zz85/blog\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Font( data ) {\n\n this.type = 'Font';\n\n this.data = data;\n\n }\n\n Object.assign( Font.prototype, {\n\n isFont: true,\n\n generateShapes: function ( text, size, divisions ) {\n\n if ( size === undefined ) size = 100;\n if ( divisions === undefined ) divisions = 4;\n\n var shapes = [];\n var paths = createPaths( text, size, divisions, this.data );\n\n for ( var p = 0, pl = paths.length; p < pl; p ++ ) {\n\n Array.prototype.push.apply( shapes, paths[ p ].toShapes() );\n\n }\n\n return shapes;\n\n }\n\n } );\n\n function createPaths( text, size, divisions, data ) {\n\n var chars = String( text ).split( '' );\n var scale = size / data.resolution;\n var line_height = ( data.boundingBox.yMax - data.boundingBox.yMin + data.underlineThickness ) * scale;\n\n var paths = [];\n\n var offsetX = 0, offsetY = 0;\n\n for ( var i = 0; i < chars.length; i ++ ) {\n\n var char = chars[ i ];\n\n if ( char === '\\n' ) {\n\n offsetX = 0;\n offsetY -= line_height;\n\n } else {\n\n var ret = createPath( char, divisions, scale, offsetX, offsetY, data );\n offsetX += ret.offsetX;\n paths.push( ret.path );\n\n }\n\n }\n\n return paths;\n\n }\n\n function createPath( char, divisions, scale, offsetX, offsetY, data ) {\n\n var glyph = data.glyphs[ char ] || data.glyphs[ '?' ];\n\n if ( ! glyph ) return;\n\n var path = new ShapePath();\n\n var x, y, cpx, cpy, cpx1, cpy1, cpx2, cpy2;\n\n if ( glyph.o ) {\n\n var outline = glyph._cachedOutline || ( glyph._cachedOutline = glyph.o.split( ' ' ) );\n\n for ( var i = 0, l = outline.length; i < l; ) {\n\n var action = outline[ i ++ ];\n\n switch ( action ) {\n\n case 'm': // moveTo\n\n x = outline[ i ++ ] * scale + offsetX;\n y = outline[ i ++ ] * scale + offsetY;\n\n path.moveTo( x, y );\n\n break;\n\n case 'l': // lineTo\n\n x = outline[ i ++ ] * scale + offsetX;\n y = outline[ i ++ ] * scale + offsetY;\n\n path.lineTo( x, y );\n\n break;\n\n case 'q': // quadraticCurveTo\n\n cpx = outline[ i ++ ] * scale + offsetX;\n cpy = outline[ i ++ ] * scale + offsetY;\n cpx1 = outline[ i ++ ] * scale + offsetX;\n cpy1 = outline[ i ++ ] * scale + offsetY;\n\n path.quadraticCurveTo( cpx1, cpy1, cpx, cpy );\n\n break;\n\n case 'b': // bezierCurveTo\n\n cpx = outline[ i ++ ] * scale + offsetX;\n cpy = outline[ i ++ ] * scale + offsetY;\n cpx1 = outline[ i ++ ] * scale + offsetX;\n cpy1 = outline[ i ++ ] * scale + offsetY;\n cpx2 = outline[ i ++ ] * scale + offsetX;\n cpy2 = outline[ i ++ ] * scale + offsetY;\n\n path.bezierCurveTo( cpx1, cpy1, cpx2, cpy2, cpx, cpy );\n\n break;\n\n }\n\n }\n\n }\n\n return { offsetX: glyph.ha * scale, path: path };\n\n }\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function FontLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( FontLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var scope = this;\n\n var loader = new FileLoader( this.manager );\n loader.setPath( this.path );\n loader.load( url, function ( text ) {\n\n var json;\n\n try {\n\n json = JSON.parse( text );\n\n } catch ( e ) {\n\n console.warn( 'THREE.FontLoader: typeface.js support is being deprecated. Use typeface.json instead.' );\n json = JSON.parse( text.substring( 65, text.length - 2 ) );\n\n }\n\n var font = scope.parse( json );\n\n if ( onLoad ) onLoad( font );\n\n }, onProgress, onError );\n\n },\n\n parse: function ( json ) {\n\n return new Font( json );\n\n },\n\n setPath: function ( value ) {\n\n this.path = value;\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n var context;\n\n var AudioContext = {\n\n getContext: function () {\n\n if ( context === undefined ) {\n\n context = new ( window.AudioContext || window.webkitAudioContext )();\n\n }\n\n return context;\n\n },\n\n setContext: function ( value ) {\n\n context = value;\n\n }\n\n };\n\n /**\n * @author Reece Aaron Lecrivain / http://reecenotes.com/\n */\n\n function AudioLoader( manager ) {\n\n this.manager = ( manager !== undefined ) ? manager : DefaultLoadingManager;\n\n }\n\n Object.assign( AudioLoader.prototype, {\n\n load: function ( url, onLoad, onProgress, onError ) {\n\n var loader = new FileLoader( this.manager );\n loader.setResponseType( 'arraybuffer' );\n loader.load( url, function ( buffer ) {\n\n var context = AudioContext.getContext();\n\n context.decodeAudioData( buffer, function ( audioBuffer ) {\n\n onLoad( audioBuffer );\n\n } );\n\n }, onProgress, onError );\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function StereoCamera() {\n\n this.type = 'StereoCamera';\n\n this.aspect = 1;\n\n this.eyeSep = 0.064;\n\n this.cameraL = new PerspectiveCamera();\n this.cameraL.layers.enable( 1 );\n this.cameraL.matrixAutoUpdate = false;\n\n this.cameraR = new PerspectiveCamera();\n this.cameraR.layers.enable( 2 );\n this.cameraR.matrixAutoUpdate = false;\n\n }\n\n Object.assign( StereoCamera.prototype, {\n\n update: ( function () {\n\n var instance, focus, fov, aspect, near, far, zoom, eyeSep;\n\n var eyeRight = new Matrix4();\n var eyeLeft = new Matrix4();\n\n return function update( camera ) {\n\n var needsUpdate = instance !== this || focus !== camera.focus || fov !== camera.fov ||\n aspect !== camera.aspect * this.aspect || near !== camera.near ||\n far !== camera.far || zoom !== camera.zoom || eyeSep !== this.eyeSep;\n\n if ( needsUpdate ) {\n\n instance = this;\n focus = camera.focus;\n fov = camera.fov;\n aspect = camera.aspect * this.aspect;\n near = camera.near;\n far = camera.far;\n zoom = camera.zoom;\n\n // Off-axis stereoscopic effect based on\n // http://paulbourke.net/stereographics/stereorender/\n\n var projectionMatrix = camera.projectionMatrix.clone();\n eyeSep = this.eyeSep / 2;\n var eyeSepOnProjection = eyeSep * near / focus;\n var ymax = ( near * Math.tan( _Math.DEG2RAD * fov * 0.5 ) ) / zoom;\n var xmin, xmax;\n\n // translate xOffset\n\n eyeLeft.elements[ 12 ] = - eyeSep;\n eyeRight.elements[ 12 ] = eyeSep;\n\n // for left eye\n\n xmin = - ymax * aspect + eyeSepOnProjection;\n xmax = ymax * aspect + eyeSepOnProjection;\n\n projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );\n projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n this.cameraL.projectionMatrix.copy( projectionMatrix );\n\n // for right eye\n\n xmin = - ymax * aspect - eyeSepOnProjection;\n xmax = ymax * aspect - eyeSepOnProjection;\n\n projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin );\n projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin );\n\n this.cameraR.projectionMatrix.copy( projectionMatrix );\n\n }\n\n this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft );\n this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight );\n\n };\n\n } )()\n\n } );\n\n /**\n * Camera for rendering cube maps\n * - renders scene into axis-aligned cube\n *\n * @author alteredq / http://alteredqualia.com/\n */\n\n function CubeCamera( near, far, cubeResolution ) {\n\n Object3D.call( this );\n\n this.type = 'CubeCamera';\n\n var fov = 90, aspect = 1;\n\n var cameraPX = new PerspectiveCamera( fov, aspect, near, far );\n cameraPX.up.set( 0, - 1, 0 );\n cameraPX.lookAt( new Vector3( 1, 0, 0 ) );\n this.add( cameraPX );\n\n var cameraNX = new PerspectiveCamera( fov, aspect, near, far );\n cameraNX.up.set( 0, - 1, 0 );\n cameraNX.lookAt( new Vector3( - 1, 0, 0 ) );\n this.add( cameraNX );\n\n var cameraPY = new PerspectiveCamera( fov, aspect, near, far );\n cameraPY.up.set( 0, 0, 1 );\n cameraPY.lookAt( new Vector3( 0, 1, 0 ) );\n this.add( cameraPY );\n\n var cameraNY = new PerspectiveCamera( fov, aspect, near, far );\n cameraNY.up.set( 0, 0, - 1 );\n cameraNY.lookAt( new Vector3( 0, - 1, 0 ) );\n this.add( cameraNY );\n\n var cameraPZ = new PerspectiveCamera( fov, aspect, near, far );\n cameraPZ.up.set( 0, - 1, 0 );\n cameraPZ.lookAt( new Vector3( 0, 0, 1 ) );\n this.add( cameraPZ );\n\n var cameraNZ = new PerspectiveCamera( fov, aspect, near, far );\n cameraNZ.up.set( 0, - 1, 0 );\n cameraNZ.lookAt( new Vector3( 0, 0, - 1 ) );\n this.add( cameraNZ );\n\n var options = { format: RGBFormat, magFilter: LinearFilter, minFilter: LinearFilter };\n\n this.renderTarget = new WebGLRenderTargetCube( cubeResolution, cubeResolution, options );\n this.renderTarget.texture.name = \"CubeCamera\";\n\n this.update = function ( renderer, scene ) {\n\n if ( this.parent === null ) this.updateMatrixWorld();\n\n var renderTarget = this.renderTarget;\n var generateMipmaps = renderTarget.texture.generateMipmaps;\n\n renderTarget.texture.generateMipmaps = false;\n\n renderTarget.activeCubeFace = 0;\n renderer.render( scene, cameraPX, renderTarget );\n\n renderTarget.activeCubeFace = 1;\n renderer.render( scene, cameraNX, renderTarget );\n\n renderTarget.activeCubeFace = 2;\n renderer.render( scene, cameraPY, renderTarget );\n\n renderTarget.activeCubeFace = 3;\n renderer.render( scene, cameraNY, renderTarget );\n\n renderTarget.activeCubeFace = 4;\n renderer.render( scene, cameraPZ, renderTarget );\n\n renderTarget.texture.generateMipmaps = generateMipmaps;\n\n renderTarget.activeCubeFace = 5;\n renderer.render( scene, cameraNZ, renderTarget );\n\n renderer.setRenderTarget( null );\n\n };\n\n this.clear = function ( renderer, color, depth, stencil ) {\n\n var renderTarget = this.renderTarget;\n\n for ( var i = 0; i < 6; i ++ ) {\n\n renderTarget.activeCubeFace = i;\n renderer.setRenderTarget( renderTarget );\n\n renderer.clear( color, depth, stencil );\n\n }\n\n renderer.setRenderTarget( null );\n\n };\n\n }\n\n CubeCamera.prototype = Object.create( Object3D.prototype );\n CubeCamera.prototype.constructor = CubeCamera;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AudioListener() {\n\n Object3D.call( this );\n\n this.type = 'AudioListener';\n\n this.context = AudioContext.getContext();\n\n this.gain = this.context.createGain();\n this.gain.connect( this.context.destination );\n\n this.filter = null;\n\n }\n\n AudioListener.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: AudioListener,\n\n getInput: function () {\n\n return this.gain;\n\n },\n\n removeFilter: function ( ) {\n\n if ( this.filter !== null ) {\n\n this.gain.disconnect( this.filter );\n this.filter.disconnect( this.context.destination );\n this.gain.connect( this.context.destination );\n this.filter = null;\n\n }\n\n },\n\n getFilter: function () {\n\n return this.filter;\n\n },\n\n setFilter: function ( value ) {\n\n if ( this.filter !== null ) {\n\n this.gain.disconnect( this.filter );\n this.filter.disconnect( this.context.destination );\n\n } else {\n\n this.gain.disconnect( this.context.destination );\n\n }\n\n this.filter = value;\n this.gain.connect( this.filter );\n this.filter.connect( this.context.destination );\n\n },\n\n getMasterVolume: function () {\n\n return this.gain.gain.value;\n\n },\n\n setMasterVolume: function ( value ) {\n\n this.gain.gain.value = value;\n\n },\n\n updateMatrixWorld: ( function () {\n\n var position = new Vector3();\n var quaternion = new Quaternion();\n var scale = new Vector3();\n\n var orientation = new Vector3();\n\n return function updateMatrixWorld( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n var listener = this.context.listener;\n var up = this.up;\n\n this.matrixWorld.decompose( position, quaternion, scale );\n\n orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion );\n\n if ( listener.positionX ) {\n\n listener.positionX.setValueAtTime( position.x, this.context.currentTime );\n listener.positionY.setValueAtTime( position.y, this.context.currentTime );\n listener.positionZ.setValueAtTime( position.z, this.context.currentTime );\n listener.forwardX.setValueAtTime( orientation.x, this.context.currentTime );\n listener.forwardY.setValueAtTime( orientation.y, this.context.currentTime );\n listener.forwardZ.setValueAtTime( orientation.z, this.context.currentTime );\n listener.upX.setValueAtTime( up.x, this.context.currentTime );\n listener.upY.setValueAtTime( up.y, this.context.currentTime );\n listener.upZ.setValueAtTime( up.z, this.context.currentTime );\n\n } else {\n\n listener.setPosition( position.x, position.y, position.z );\n listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z );\n\n }\n\n };\n\n } )()\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Reece Aaron Lecrivain / http://reecenotes.com/\n */\n\n function Audio( listener ) {\n\n Object3D.call( this );\n\n this.type = 'Audio';\n\n this.context = listener.context;\n\n this.gain = this.context.createGain();\n this.gain.connect( listener.getInput() );\n\n this.autoplay = false;\n\n this.buffer = null;\n this.loop = false;\n this.startTime = 0;\n this.offset = 0;\n this.playbackRate = 1;\n this.isPlaying = false;\n this.hasPlaybackControl = true;\n this.sourceType = 'empty';\n\n this.filters = [];\n\n }\n\n Audio.prototype = Object.assign( Object.create( Object3D.prototype ), {\n\n constructor: Audio,\n\n getOutput: function () {\n\n return this.gain;\n\n },\n\n setNodeSource: function ( audioNode ) {\n\n this.hasPlaybackControl = false;\n this.sourceType = 'audioNode';\n this.source = audioNode;\n this.connect();\n\n return this;\n\n },\n\n setBuffer: function ( audioBuffer ) {\n\n this.buffer = audioBuffer;\n this.sourceType = 'buffer';\n\n if ( this.autoplay ) this.play();\n\n return this;\n\n },\n\n play: function () {\n\n if ( this.isPlaying === true ) {\n\n console.warn( 'THREE.Audio: Audio is already playing.' );\n return;\n\n }\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n var source = this.context.createBufferSource();\n\n source.buffer = this.buffer;\n source.loop = this.loop;\n source.onended = this.onEnded.bind( this );\n source.playbackRate.setValueAtTime( this.playbackRate, this.startTime );\n this.startTime = this.context.currentTime;\n source.start( this.startTime, this.offset );\n\n this.isPlaying = true;\n\n this.source = source;\n\n return this.connect();\n\n },\n\n pause: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n if ( this.isPlaying === true ) {\n\n this.source.stop();\n this.offset += ( this.context.currentTime - this.startTime ) * this.playbackRate;\n this.isPlaying = false;\n\n }\n\n return this;\n\n },\n\n stop: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.source.stop();\n this.offset = 0;\n this.isPlaying = false;\n\n return this;\n\n },\n\n connect: function () {\n\n if ( this.filters.length > 0 ) {\n\n this.source.connect( this.filters[ 0 ] );\n\n for ( var i = 1, l = this.filters.length; i < l; i ++ ) {\n\n this.filters[ i - 1 ].connect( this.filters[ i ] );\n\n }\n\n this.filters[ this.filters.length - 1 ].connect( this.getOutput() );\n\n } else {\n\n this.source.connect( this.getOutput() );\n\n }\n\n return this;\n\n },\n\n disconnect: function () {\n\n if ( this.filters.length > 0 ) {\n\n this.source.disconnect( this.filters[ 0 ] );\n\n for ( var i = 1, l = this.filters.length; i < l; i ++ ) {\n\n this.filters[ i - 1 ].disconnect( this.filters[ i ] );\n\n }\n\n this.filters[ this.filters.length - 1 ].disconnect( this.getOutput() );\n\n } else {\n\n this.source.disconnect( this.getOutput() );\n\n }\n\n return this;\n\n },\n\n getFilters: function () {\n\n return this.filters;\n\n },\n\n setFilters: function ( value ) {\n\n if ( ! value ) value = [];\n\n if ( this.isPlaying === true ) {\n\n this.disconnect();\n this.filters = value;\n this.connect();\n\n } else {\n\n this.filters = value;\n\n }\n\n return this;\n\n },\n\n getFilter: function () {\n\n return this.getFilters()[ 0 ];\n\n },\n\n setFilter: function ( filter ) {\n\n return this.setFilters( filter ? [ filter ] : [] );\n\n },\n\n setPlaybackRate: function ( value ) {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.playbackRate = value;\n\n if ( this.isPlaying === true ) {\n\n this.source.playbackRate.setValueAtTime( this.playbackRate, this.context.currentTime );\n\n }\n\n return this;\n\n },\n\n getPlaybackRate: function () {\n\n return this.playbackRate;\n\n },\n\n onEnded: function () {\n\n this.isPlaying = false;\n\n },\n\n getLoop: function () {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return false;\n\n }\n\n return this.loop;\n\n },\n\n setLoop: function ( value ) {\n\n if ( this.hasPlaybackControl === false ) {\n\n console.warn( 'THREE.Audio: this Audio has no playback control.' );\n return;\n\n }\n\n this.loop = value;\n\n if ( this.isPlaying === true ) {\n\n this.source.loop = this.loop;\n\n }\n\n return this;\n\n },\n\n getVolume: function () {\n\n return this.gain.gain.value;\n\n },\n\n setVolume: function ( value ) {\n\n this.gain.gain.value = value;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function PositionalAudio( listener ) {\n\n Audio.call( this, listener );\n\n this.panner = this.context.createPanner();\n this.panner.connect( this.gain );\n\n }\n\n PositionalAudio.prototype = Object.assign( Object.create( Audio.prototype ), {\n\n constructor: PositionalAudio,\n\n getOutput: function () {\n\n return this.panner;\n\n },\n\n getRefDistance: function () {\n\n return this.panner.refDistance;\n\n },\n\n setRefDistance: function ( value ) {\n\n this.panner.refDistance = value;\n\n },\n\n getRolloffFactor: function () {\n\n return this.panner.rolloffFactor;\n\n },\n\n setRolloffFactor: function ( value ) {\n\n this.panner.rolloffFactor = value;\n\n },\n\n getDistanceModel: function () {\n\n return this.panner.distanceModel;\n\n },\n\n setDistanceModel: function ( value ) {\n\n this.panner.distanceModel = value;\n\n },\n\n getMaxDistance: function () {\n\n return this.panner.maxDistance;\n\n },\n\n setMaxDistance: function ( value ) {\n\n this.panner.maxDistance = value;\n\n },\n\n updateMatrixWorld: ( function () {\n\n var position = new Vector3();\n\n return function updateMatrixWorld( force ) {\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n position.setFromMatrixPosition( this.matrixWorld );\n\n this.panner.setPosition( position.x, position.y, position.z );\n\n };\n\n } )()\n\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AudioAnalyser( audio, fftSize ) {\n\n this.analyser = audio.context.createAnalyser();\n this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048;\n\n this.data = new Uint8Array( this.analyser.frequencyBinCount );\n\n audio.getOutput().connect( this.analyser );\n\n }\n\n Object.assign( AudioAnalyser.prototype, {\n\n getFrequencyData: function () {\n\n this.analyser.getByteFrequencyData( this.data );\n\n return this.data;\n\n },\n\n getAverageFrequency: function () {\n\n var value = 0, data = this.getFrequencyData();\n\n for ( var i = 0; i < data.length; i ++ ) {\n\n value += data[ i ];\n\n }\n\n return value / data.length;\n\n }\n\n } );\n\n /**\n *\n * Buffered scene graph property that allows weighted accumulation.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function PropertyMixer( binding, typeName, valueSize ) {\n\n this.binding = binding;\n this.valueSize = valueSize;\n\n var bufferType = Float64Array,\n mixFunction;\n\n switch ( typeName ) {\n\n case 'quaternion':\n mixFunction = this._slerp;\n break;\n\n case 'string':\n case 'bool':\n bufferType = Array;\n mixFunction = this._select;\n break;\n\n default:\n mixFunction = this._lerp;\n\n }\n\n this.buffer = new bufferType( valueSize * 4 );\n // layout: [ incoming | accu0 | accu1 | orig ]\n //\n // interpolators can use .buffer as their .result\n // the data then goes to 'incoming'\n //\n // 'accu0' and 'accu1' are used frame-interleaved for\n // the cumulative result and are compared to detect\n // changes\n //\n // 'orig' stores the original state of the property\n\n this._mixBufferRegion = mixFunction;\n\n this.cumulativeWeight = 0;\n\n this.useCount = 0;\n this.referenceCount = 0;\n\n }\n\n Object.assign( PropertyMixer.prototype, {\n\n // accumulate data in the 'incoming' region into 'accu<i>'\n accumulate: function ( accuIndex, weight ) {\n\n // note: happily accumulating nothing when weight = 0, the caller knows\n // the weight and shouldn't have made the call in the first place\n\n var buffer = this.buffer,\n stride = this.valueSize,\n offset = accuIndex * stride + stride,\n\n currentWeight = this.cumulativeWeight;\n\n if ( currentWeight === 0 ) {\n\n // accuN := incoming * weight\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n buffer[ offset + i ] = buffer[ i ];\n\n }\n\n currentWeight = weight;\n\n } else {\n\n // accuN := accuN + incoming * weight\n\n currentWeight += weight;\n var mix = weight / currentWeight;\n this._mixBufferRegion( buffer, offset, 0, mix, stride );\n\n }\n\n this.cumulativeWeight = currentWeight;\n\n },\n\n // apply the state of 'accu<i>' to the binding when accus differ\n apply: function ( accuIndex ) {\n\n var stride = this.valueSize,\n buffer = this.buffer,\n offset = accuIndex * stride + stride,\n\n weight = this.cumulativeWeight,\n\n binding = this.binding;\n\n this.cumulativeWeight = 0;\n\n if ( weight < 1 ) {\n\n // accuN := accuN + original * ( 1 - cumulativeWeight )\n\n var originalValueOffset = stride * 3;\n\n this._mixBufferRegion(\n buffer, offset, originalValueOffset, 1 - weight, stride );\n\n }\n\n for ( var i = stride, e = stride + stride; i !== e; ++ i ) {\n\n if ( buffer[ i ] !== buffer[ i + stride ] ) {\n\n // value has changed -> update scene graph\n\n binding.setValue( buffer, offset );\n break;\n\n }\n\n }\n\n },\n\n // remember the state of the bound property and copy it to both accus\n saveOriginalState: function () {\n\n var binding = this.binding;\n\n var buffer = this.buffer,\n stride = this.valueSize,\n\n originalValueOffset = stride * 3;\n\n binding.getValue( buffer, originalValueOffset );\n\n // accu[0..1] := orig -- initially detect changes against the original\n for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) {\n\n buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ];\n\n }\n\n this.cumulativeWeight = 0;\n\n },\n\n // apply the state previously taken via 'saveOriginalState' to the binding\n restoreOriginalState: function () {\n\n var originalValueOffset = this.valueSize * 3;\n this.binding.setValue( this.buffer, originalValueOffset );\n\n },\n\n\n // mix functions\n\n _select: function ( buffer, dstOffset, srcOffset, t, stride ) {\n\n if ( t >= 0.5 ) {\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n buffer[ dstOffset + i ] = buffer[ srcOffset + i ];\n\n }\n\n }\n\n },\n\n _slerp: function ( buffer, dstOffset, srcOffset, t ) {\n\n Quaternion.slerpFlat( buffer, dstOffset, buffer, dstOffset, buffer, srcOffset, t );\n\n },\n\n _lerp: function ( buffer, dstOffset, srcOffset, t, stride ) {\n\n var s = 1 - t;\n\n for ( var i = 0; i !== stride; ++ i ) {\n\n var j = dstOffset + i;\n\n buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t;\n\n }\n\n }\n\n } );\n\n /**\n *\n * A reference to a real property in the scene graph.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n // Characters [].:/ are reserved for track binding syntax.\n var RESERVED_CHARS_RE = '\\\\[\\\\]\\\\.:\\\\/';\n\n function Composite( targetGroup, path, optionalParsedPath ) {\n\n var parsedPath = optionalParsedPath || PropertyBinding.parseTrackName( path );\n\n this._targetGroup = targetGroup;\n this._bindings = targetGroup.subscribe_( path, parsedPath );\n\n }\n\n Object.assign( Composite.prototype, {\n\n getValue: function ( array, offset ) {\n\n this.bind(); // bind all binding\n\n var firstValidIndex = this._targetGroup.nCachedObjects_,\n binding = this._bindings[ firstValidIndex ];\n\n // and only call .getValue on the first\n if ( binding !== undefined ) binding.getValue( array, offset );\n\n },\n\n setValue: function ( array, offset ) {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].setValue( array, offset );\n\n }\n\n },\n\n bind: function () {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].bind();\n\n }\n\n },\n\n unbind: function () {\n\n var bindings = this._bindings;\n\n for ( var i = this._targetGroup.nCachedObjects_,\n n = bindings.length; i !== n; ++ i ) {\n\n bindings[ i ].unbind();\n\n }\n\n }\n\n } );\n\n\n function PropertyBinding( rootNode, path, parsedPath ) {\n\n this.path = path;\n this.parsedPath = parsedPath || PropertyBinding.parseTrackName( path );\n\n this.node = PropertyBinding.findNode( rootNode, this.parsedPath.nodeName ) || rootNode;\n\n this.rootNode = rootNode;\n\n }\n\n Object.assign( PropertyBinding, {\n\n Composite: Composite,\n\n create: function ( root, path, parsedPath ) {\n\n if ( ! ( root && root.isAnimationObjectGroup ) ) {\n\n return new PropertyBinding( root, path, parsedPath );\n\n } else {\n\n return new PropertyBinding.Composite( root, path, parsedPath );\n\n }\n\n },\n\n /**\n * Replaces spaces with underscores and removes unsupported characters from\n * node names, to ensure compatibility with parseTrackName().\n *\n * @param {string} name Node name to be sanitized.\n * @return {string}\n */\n sanitizeNodeName: ( function () {\n\n var reservedRe = new RegExp( '[' + RESERVED_CHARS_RE + ']', 'g' );\n\n return function sanitizeNodeName( name ) {\n\n return name.replace( /\\s/g, '_' ).replace( reservedRe, '' );\n\n };\n\n }() ),\n\n parseTrackName: function () {\n\n // Attempts to allow node names from any language. ES5's w regexp matches\n // only latin characters, and the unicode \\p{L} is not yet supported. So\n // instead, we exclude reserved characters and match everything else.\n var wordChar = '[^' + RESERVED_CHARS_RE + ']';\n var wordCharOrDot = '[^' + RESERVED_CHARS_RE.replace( '\\\\.', '' ) + ']';\n\n // Parent directories, delimited by '/' or ':'. Currently unused, but must\n // be matched to parse the rest of the track name.\n var directoryRe = /((?:WC+[\\/:])*)/.source.replace( 'WC', wordChar );\n\n // Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.\n var nodeRe = /(WCOD+)?/.source.replace( 'WCOD', wordCharOrDot );\n\n // Object on target node, and accessor. May not contain reserved\n // characters. Accessor may contain any character except closing bracket.\n var objectRe = /(?:\\.(WC+)(?:\\[(.+)\\])?)?/.source.replace( 'WC', wordChar );\n\n // Property and accessor. May not contain reserved characters. Accessor may\n // contain any non-bracket characters.\n var propertyRe = /\\.(WC+)(?:\\[(.+)\\])?/.source.replace( 'WC', wordChar );\n\n var trackRe = new RegExp( ''\n + '^'\n + directoryRe\n + nodeRe\n + objectRe\n + propertyRe\n + '$'\n );\n\n var supportedObjectNames = [ 'material', 'materials', 'bones' ];\n\n return function parseTrackName( trackName ) {\n\n var matches = trackRe.exec( trackName );\n\n if ( ! matches ) {\n\n throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );\n\n }\n\n var results = {\n // directoryName: matches[ 1 ], // (tschw) currently unused\n nodeName: matches[ 2 ],\n objectName: matches[ 3 ],\n objectIndex: matches[ 4 ],\n propertyName: matches[ 5 ], // required\n propertyIndex: matches[ 6 ]\n };\n\n var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );\n\n if ( lastDot !== undefined && lastDot !== - 1 ) {\n\n var objectName = results.nodeName.substring( lastDot + 1 );\n\n // Object names must be checked against a whitelist. Otherwise, there\n // is no way to parse 'foo.bar.baz': 'baz' must be a property, but\n // 'bar' could be the objectName, or part of a nodeName (which can\n // include '.' characters).\n if ( supportedObjectNames.indexOf( objectName ) !== - 1 ) {\n\n results.nodeName = results.nodeName.substring( 0, lastDot );\n results.objectName = objectName;\n\n }\n\n }\n\n if ( results.propertyName === null || results.propertyName.length === 0 ) {\n\n throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );\n\n }\n\n return results;\n\n };\n\n }(),\n\n findNode: function ( root, nodeName ) {\n\n if ( ! nodeName || nodeName === \"\" || nodeName === \"root\" || nodeName === \".\" || nodeName === - 1 || nodeName === root.name || nodeName === root.uuid ) {\n\n return root;\n\n }\n\n // search into skeleton bones.\n if ( root.skeleton ) {\n\n var bone = root.skeleton.getBoneByName( nodeName );\n\n if ( bone !== undefined ) {\n\n return bone;\n\n }\n\n }\n\n // search into node subtree.\n if ( root.children ) {\n\n var searchNodeSubtree = function ( children ) {\n\n for ( var i = 0; i < children.length; i ++ ) {\n\n var childNode = children[ i ];\n\n if ( childNode.name === nodeName || childNode.uuid === nodeName ) {\n\n return childNode;\n\n }\n\n var result = searchNodeSubtree( childNode.children );\n\n if ( result ) return result;\n\n }\n\n return null;\n\n };\n\n var subTreeNode = searchNodeSubtree( root.children );\n\n if ( subTreeNode ) {\n\n return subTreeNode;\n\n }\n\n }\n\n return null;\n\n }\n\n } );\n\n Object.assign( PropertyBinding.prototype, { // prototype, continued\n\n // these are used to \"bind\" a nonexistent property\n _getValue_unavailable: function () {},\n _setValue_unavailable: function () {},\n\n BindingType: {\n Direct: 0,\n EntireArray: 1,\n ArrayElement: 2,\n HasFromToArray: 3\n },\n\n Versioning: {\n None: 0,\n NeedsUpdate: 1,\n MatrixWorldNeedsUpdate: 2\n },\n\n GetterByBindingType: [\n\n function getValue_direct( buffer, offset ) {\n\n buffer[ offset ] = this.node[ this.propertyName ];\n\n },\n\n function getValue_array( buffer, offset ) {\n\n var source = this.resolvedProperty;\n\n for ( var i = 0, n = source.length; i !== n; ++ i ) {\n\n buffer[ offset ++ ] = source[ i ];\n\n }\n\n },\n\n function getValue_arrayElement( buffer, offset ) {\n\n buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ];\n\n },\n\n function getValue_toArray( buffer, offset ) {\n\n this.resolvedProperty.toArray( buffer, offset );\n\n }\n\n ],\n\n SetterByBindingTypeAndVersioning: [\n\n [\n // Direct\n\n function setValue_direct( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n\n },\n\n function setValue_direct_setNeedsUpdate( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.targetObject[ this.propertyName ] = buffer[ offset ];\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // EntireArray\n\n function setValue_array( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n },\n\n function setValue_array_setNeedsUpdate( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n var dest = this.resolvedProperty;\n\n for ( var i = 0, n = dest.length; i !== n; ++ i ) {\n\n dest[ i ] = buffer[ offset ++ ];\n\n }\n\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // ArrayElement\n\n function setValue_arrayElement( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n\n },\n\n function setValue_arrayElement_setNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ];\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ], [\n\n // HasToFromArray\n\n function setValue_fromArray( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n\n },\n\n function setValue_fromArray_setNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n this.targetObject.needsUpdate = true;\n\n },\n\n function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) {\n\n this.resolvedProperty.fromArray( buffer, offset );\n this.targetObject.matrixWorldNeedsUpdate = true;\n\n }\n\n ]\n\n ],\n\n getValue: function getValue_unbound( targetArray, offset ) {\n\n this.bind();\n this.getValue( targetArray, offset );\n\n // Note: This class uses a State pattern on a per-method basis:\n // 'bind' sets 'this.getValue' / 'setValue' and shadows the\n // prototype version of these methods with one that represents\n // the bound state. When the property is not found, the methods\n // become no-ops.\n\n },\n\n setValue: function getValue_unbound( sourceArray, offset ) {\n\n this.bind();\n this.setValue( sourceArray, offset );\n\n },\n\n // create getter / setter pair for a property in the scene graph\n bind: function () {\n\n var targetObject = this.node,\n parsedPath = this.parsedPath,\n\n objectName = parsedPath.objectName,\n propertyName = parsedPath.propertyName,\n propertyIndex = parsedPath.propertyIndex;\n\n if ( ! targetObject ) {\n\n targetObject = PropertyBinding.findNode( this.rootNode, parsedPath.nodeName ) || this.rootNode;\n\n this.node = targetObject;\n\n }\n\n // set fail state so we can just 'return' on error\n this.getValue = this._getValue_unavailable;\n this.setValue = this._setValue_unavailable;\n\n // ensure there is a value node\n if ( ! targetObject ) {\n\n console.error( 'THREE.PropertyBinding: Trying to update node for track: ' + this.path + ' but it wasn\\'t found.' );\n return;\n\n }\n\n if ( objectName ) {\n\n var objectIndex = parsedPath.objectIndex;\n\n // special cases were we need to reach deeper into the hierarchy to get the face materials....\n switch ( objectName ) {\n\n case 'materials':\n\n if ( ! targetObject.material ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to material as node does not have a material.', this );\n return;\n\n }\n\n if ( ! targetObject.material.materials ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to material.materials as node.material does not have a materials array.', this );\n return;\n\n }\n\n targetObject = targetObject.material.materials;\n\n break;\n\n case 'bones':\n\n if ( ! targetObject.skeleton ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to bones as node does not have a skeleton.', this );\n return;\n\n }\n\n // potential future optimization: skip this if propertyIndex is already an integer\n // and convert the integer string to a true integer.\n\n targetObject = targetObject.skeleton.bones;\n\n // support resolving morphTarget names into indices.\n for ( var i = 0; i < targetObject.length; i ++ ) {\n\n if ( targetObject[ i ].name === objectIndex ) {\n\n objectIndex = i;\n break;\n\n }\n\n }\n\n break;\n\n default:\n\n if ( targetObject[ objectName ] === undefined ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to objectName of node undefined.', this );\n return;\n\n }\n\n targetObject = targetObject[ objectName ];\n\n }\n\n\n if ( objectIndex !== undefined ) {\n\n if ( targetObject[ objectIndex ] === undefined ) {\n\n console.error( 'THREE.PropertyBinding: Trying to bind to objectIndex of objectName, but is undefined.', this, targetObject );\n return;\n\n }\n\n targetObject = targetObject[ objectIndex ];\n\n }\n\n }\n\n // resolve property\n var nodeProperty = targetObject[ propertyName ];\n\n if ( nodeProperty === undefined ) {\n\n var nodeName = parsedPath.nodeName;\n\n console.error( 'THREE.PropertyBinding: Trying to update property for track: ' + nodeName +\n '.' + propertyName + ' but it wasn\\'t found.', targetObject );\n return;\n\n }\n\n // determine versioning scheme\n var versioning = this.Versioning.None;\n\n if ( targetObject.needsUpdate !== undefined ) { // material\n\n versioning = this.Versioning.NeedsUpdate;\n this.targetObject = targetObject;\n\n } else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform\n\n versioning = this.Versioning.MatrixWorldNeedsUpdate;\n this.targetObject = targetObject;\n\n }\n\n // determine how the property gets bound\n var bindingType = this.BindingType.Direct;\n\n if ( propertyIndex !== undefined ) {\n\n // access a sub element of the property array (only primitives are supported right now)\n\n if ( propertyName === \"morphTargetInfluences\" ) {\n\n // potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer.\n\n // support resolving morphTarget names into indices.\n if ( ! targetObject.geometry ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.', this );\n return;\n\n }\n\n if ( targetObject.geometry.isBufferGeometry ) {\n\n if ( ! targetObject.geometry.morphAttributes ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphAttributes.', this );\n return;\n\n }\n\n for ( var i = 0; i < this.node.geometry.morphAttributes.position.length; i ++ ) {\n\n if ( targetObject.geometry.morphAttributes.position[ i ].name === propertyIndex ) {\n\n propertyIndex = i;\n break;\n\n }\n\n }\n\n\n } else {\n\n if ( ! targetObject.geometry.morphTargets ) {\n\n console.error( 'THREE.PropertyBinding: Can not bind to morphTargetInfluences because node does not have a geometry.morphTargets.', this );\n return;\n\n }\n\n for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) {\n\n if ( targetObject.geometry.morphTargets[ i ].name === propertyIndex ) {\n\n propertyIndex = i;\n break;\n\n }\n\n }\n\n }\n\n }\n\n bindingType = this.BindingType.ArrayElement;\n\n this.resolvedProperty = nodeProperty;\n this.propertyIndex = propertyIndex;\n\n } else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) {\n\n // must use copy for Object3D.Euler/Quaternion\n\n bindingType = this.BindingType.HasFromToArray;\n\n this.resolvedProperty = nodeProperty;\n\n } else if ( Array.isArray( nodeProperty ) ) {\n\n bindingType = this.BindingType.EntireArray;\n\n this.resolvedProperty = nodeProperty;\n\n } else {\n\n this.propertyName = propertyName;\n\n }\n\n // select getter / setter\n this.getValue = this.GetterByBindingType[ bindingType ];\n this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ];\n\n },\n\n unbind: function () {\n\n this.node = null;\n\n // back to the prototype version of getValue / setValue\n // note: avoiding to mutate the shape of 'this' via 'delete'\n this.getValue = this._getValue_unbound;\n this.setValue = this._setValue_unbound;\n\n }\n\n } );\n\n //!\\ DECLARE ALIAS AFTER assign prototype !\n Object.assign( PropertyBinding.prototype, {\n\n // initial state of these methods that calls 'bind'\n _getValue_unbound: PropertyBinding.prototype.getValue,\n _setValue_unbound: PropertyBinding.prototype.setValue,\n\n } );\n\n /**\n *\n * A group of objects that receives a shared animation state.\n *\n * Usage:\n *\n * - Add objects you would otherwise pass as 'root' to the\n * constructor or the .clipAction method of AnimationMixer.\n *\n * - Instead pass this object as 'root'.\n *\n * - You can also add and remove objects later when the mixer\n * is running.\n *\n * Note:\n *\n * Objects of this class appear as one object to the mixer,\n * so cache control of the individual objects must be done\n * on the group.\n *\n * Limitation:\n *\n * - The animated properties must be compatible among the\n * all objects in the group.\n *\n * - A single property can either be controlled through a\n * target group or directly, but not both.\n *\n * @author tschw\n */\n\n function AnimationObjectGroup() {\n\n this.uuid = _Math.generateUUID();\n\n // cached objects followed by the active ones\n this._objects = Array.prototype.slice.call( arguments );\n\n this.nCachedObjects_ = 0; // threshold\n // note: read by PropertyBinding.Composite\n\n var indices = {};\n this._indicesByUUID = indices; // for bookkeeping\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n indices[ arguments[ i ].uuid ] = i;\n\n }\n\n this._paths = []; // inside: string\n this._parsedPaths = []; // inside: { we don't care, here }\n this._bindings = []; // inside: Array< PropertyBinding >\n this._bindingsIndicesByPath = {}; // inside: indices in these arrays\n\n var scope = this;\n\n this.stats = {\n\n objects: {\n get total() {\n\n return scope._objects.length;\n\n },\n get inUse() {\n\n return this.total - scope.nCachedObjects_;\n\n }\n },\n get bindingsPerObject() {\n\n return scope._bindings.length;\n\n }\n\n };\n\n }\n\n Object.assign( AnimationObjectGroup.prototype, {\n\n isAnimationObjectGroup: true,\n\n add: function () {\n\n var objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n paths = this._paths,\n parsedPaths = this._parsedPaths,\n bindings = this._bindings,\n nBindings = bindings.length,\n knownObject = undefined;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index === undefined ) {\n\n // unknown object -> add it to the ACTIVE region\n\n index = nObjects ++;\n indicesByUUID[ uuid ] = index;\n objects.push( object );\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n bindings[ j ].push( new PropertyBinding( object, paths[ j ], parsedPaths[ j ] ) );\n\n }\n\n } else if ( index < nCachedObjects ) {\n\n knownObject = objects[ index ];\n\n // move existing object to the ACTIVE region\n\n var firstActiveIndex = -- nCachedObjects,\n lastCachedObject = objects[ firstActiveIndex ];\n\n indicesByUUID[ lastCachedObject.uuid ] = index;\n objects[ index ] = lastCachedObject;\n\n indicesByUUID[ uuid ] = firstActiveIndex;\n objects[ firstActiveIndex ] = object;\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n lastCached = bindingsForPath[ firstActiveIndex ],\n binding = bindingsForPath[ index ];\n\n bindingsForPath[ index ] = lastCached;\n\n if ( binding === undefined ) {\n\n // since we do not bother to create new bindings\n // for objects that are cached, the binding may\n // or may not exist\n\n binding = new PropertyBinding( object, paths[ j ], parsedPaths[ j ] );\n\n }\n\n bindingsForPath[ firstActiveIndex ] = binding;\n\n }\n\n } else if ( objects[ index ] !== knownObject ) {\n\n console.error( 'THREE.AnimationObjectGroup: Different objects with the same UUID ' +\n 'detected. Clean the caches or recreate your infrastructure when reloading scenes.' );\n\n } // else the object is already where we want it to be\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n remove: function () {\n\n var objects = this._objects,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n bindings = this._bindings,\n nBindings = bindings.length;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index !== undefined && index >= nCachedObjects ) {\n\n // move existing object into the CACHED region\n\n var lastCachedIndex = nCachedObjects ++,\n firstActiveObject = objects[ lastCachedIndex ];\n\n indicesByUUID[ firstActiveObject.uuid ] = index;\n objects[ index ] = firstActiveObject;\n\n indicesByUUID[ uuid ] = lastCachedIndex;\n objects[ lastCachedIndex ] = object;\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n firstActive = bindingsForPath[ lastCachedIndex ],\n binding = bindingsForPath[ index ];\n\n bindingsForPath[ index ] = firstActive;\n bindingsForPath[ lastCachedIndex ] = binding;\n\n }\n\n }\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n // remove & forget\n uncache: function () {\n\n var objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n indicesByUUID = this._indicesByUUID,\n bindings = this._bindings,\n nBindings = bindings.length;\n\n for ( var i = 0, n = arguments.length; i !== n; ++ i ) {\n\n var object = arguments[ i ],\n uuid = object.uuid,\n index = indicesByUUID[ uuid ];\n\n if ( index !== undefined ) {\n\n delete indicesByUUID[ uuid ];\n\n if ( index < nCachedObjects ) {\n\n // object is cached, shrink the CACHED region\n\n var firstActiveIndex = -- nCachedObjects,\n lastCachedObject = objects[ firstActiveIndex ],\n lastIndex = -- nObjects,\n lastObject = objects[ lastIndex ];\n\n // last cached object takes this object's place\n indicesByUUID[ lastCachedObject.uuid ] = index;\n objects[ index ] = lastCachedObject;\n\n // last object goes to the activated slot and pop\n indicesByUUID[ lastObject.uuid ] = firstActiveIndex;\n objects[ firstActiveIndex ] = lastObject;\n objects.pop();\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ],\n lastCached = bindingsForPath[ firstActiveIndex ],\n last = bindingsForPath[ lastIndex ];\n\n bindingsForPath[ index ] = lastCached;\n bindingsForPath[ firstActiveIndex ] = last;\n bindingsForPath.pop();\n\n }\n\n } else {\n\n // object is active, just swap with the last and pop\n\n var lastIndex = -- nObjects,\n lastObject = objects[ lastIndex ];\n\n indicesByUUID[ lastObject.uuid ] = index;\n objects[ index ] = lastObject;\n objects.pop();\n\n // accounting is done, now do the same for all bindings\n\n for ( var j = 0, m = nBindings; j !== m; ++ j ) {\n\n var bindingsForPath = bindings[ j ];\n\n bindingsForPath[ index ] = bindingsForPath[ lastIndex ];\n bindingsForPath.pop();\n\n }\n\n } // cached or active\n\n } // if object is known\n\n } // for arguments\n\n this.nCachedObjects_ = nCachedObjects;\n\n },\n\n // Internal interface used by befriended PropertyBinding.Composite:\n\n subscribe_: function ( path, parsedPath ) {\n\n // returns an array of bindings for the given path that is changed\n // according to the contained objects in the group\n\n var indicesByPath = this._bindingsIndicesByPath,\n index = indicesByPath[ path ],\n bindings = this._bindings;\n\n if ( index !== undefined ) return bindings[ index ];\n\n var paths = this._paths,\n parsedPaths = this._parsedPaths,\n objects = this._objects,\n nObjects = objects.length,\n nCachedObjects = this.nCachedObjects_,\n bindingsForPath = new Array( nObjects );\n\n index = bindings.length;\n\n indicesByPath[ path ] = index;\n\n paths.push( path );\n parsedPaths.push( parsedPath );\n bindings.push( bindingsForPath );\n\n for ( var i = nCachedObjects, n = objects.length; i !== n; ++ i ) {\n\n var object = objects[ i ];\n bindingsForPath[ i ] = new PropertyBinding( object, path, parsedPath );\n\n }\n\n return bindingsForPath;\n\n },\n\n unsubscribe_: function ( path ) {\n\n // tells the group to forget about a property path and no longer\n // update the array previously obtained with 'subscribe_'\n\n var indicesByPath = this._bindingsIndicesByPath,\n index = indicesByPath[ path ];\n\n if ( index !== undefined ) {\n\n var paths = this._paths,\n parsedPaths = this._parsedPaths,\n bindings = this._bindings,\n lastBindingsIndex = bindings.length - 1,\n lastBindings = bindings[ lastBindingsIndex ],\n lastBindingsPath = path[ lastBindingsIndex ];\n\n indicesByPath[ lastBindingsPath ] = index;\n\n bindings[ index ] = lastBindings;\n bindings.pop();\n\n parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ];\n parsedPaths.pop();\n\n paths[ index ] = paths[ lastBindingsIndex ];\n paths.pop();\n\n }\n\n }\n\n } );\n\n /**\n *\n * Action provided by AnimationMixer for scheduling clip playback on specific\n * objects.\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n *\n */\n\n function AnimationAction( mixer, clip, localRoot ) {\n\n this._mixer = mixer;\n this._clip = clip;\n this._localRoot = localRoot || null;\n\n var tracks = clip.tracks,\n nTracks = tracks.length,\n interpolants = new Array( nTracks );\n\n var interpolantSettings = {\n endingStart: ZeroCurvatureEnding,\n endingEnd: ZeroCurvatureEnding\n };\n\n for ( var i = 0; i !== nTracks; ++ i ) {\n\n var interpolant = tracks[ i ].createInterpolant( null );\n interpolants[ i ] = interpolant;\n interpolant.settings = interpolantSettings;\n\n }\n\n this._interpolantSettings = interpolantSettings;\n\n this._interpolants = interpolants; // bound by the mixer\n\n // inside: PropertyMixer (managed by the mixer)\n this._propertyBindings = new Array( nTracks );\n\n this._cacheIndex = null; // for the memory manager\n this._byClipCacheIndex = null; // for the memory manager\n\n this._timeScaleInterpolant = null;\n this._weightInterpolant = null;\n\n this.loop = LoopRepeat;\n this._loopCount = - 1;\n\n // global mixer time when the action is to be started\n // it's set back to 'null' upon start of the action\n this._startTime = null;\n\n // scaled local time of the action\n // gets clamped or wrapped to 0..clip.duration according to loop\n this.time = 0;\n\n this.timeScale = 1;\n this._effectiveTimeScale = 1;\n\n this.weight = 1;\n this._effectiveWeight = 1;\n\n this.repetitions = Infinity; // no. of repetitions when looping\n\n this.paused = false; // true -> zero effective time scale\n this.enabled = true; // false -> zero effective weight\n\n this.clampWhenFinished = false; // keep feeding the last frame?\n\n this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate\n this.zeroSlopeAtEnd = true; // clips for start, loop and end\n\n }\n\n Object.assign( AnimationAction.prototype, {\n\n // State & Scheduling\n\n play: function () {\n\n this._mixer._activateAction( this );\n\n return this;\n\n },\n\n stop: function () {\n\n this._mixer._deactivateAction( this );\n\n return this.reset();\n\n },\n\n reset: function () {\n\n this.paused = false;\n this.enabled = true;\n\n this.time = 0; // restart clip\n this._loopCount = - 1; // forget previous loops\n this._startTime = null; // forget scheduling\n\n return this.stopFading().stopWarping();\n\n },\n\n isRunning: function () {\n\n return this.enabled && ! this.paused && this.timeScale !== 0 &&\n this._startTime === null && this._mixer._isActiveAction( this );\n\n },\n\n // return true when play has been called\n isScheduled: function () {\n\n return this._mixer._isActiveAction( this );\n\n },\n\n startAt: function ( time ) {\n\n this._startTime = time;\n\n return this;\n\n },\n\n setLoop: function ( mode, repetitions ) {\n\n this.loop = mode;\n this.repetitions = repetitions;\n\n return this;\n\n },\n\n // Weight\n\n // set the weight stopping any scheduled fading\n // although .enabled = false yields an effective weight of zero, this\n // method does *not* change .enabled, because it would be confusing\n setEffectiveWeight: function ( weight ) {\n\n this.weight = weight;\n\n // note: same logic as when updated at runtime\n this._effectiveWeight = this.enabled ? weight : 0;\n\n return this.stopFading();\n\n },\n\n // return the weight considering fading and .enabled\n getEffectiveWeight: function () {\n\n return this._effectiveWeight;\n\n },\n\n fadeIn: function ( duration ) {\n\n return this._scheduleFading( duration, 0, 1 );\n\n },\n\n fadeOut: function ( duration ) {\n\n return this._scheduleFading( duration, 1, 0 );\n\n },\n\n crossFadeFrom: function ( fadeOutAction, duration, warp ) {\n\n fadeOutAction.fadeOut( duration );\n this.fadeIn( duration );\n\n if ( warp ) {\n\n var fadeInDuration = this._clip.duration,\n fadeOutDuration = fadeOutAction._clip.duration,\n\n startEndRatio = fadeOutDuration / fadeInDuration,\n endStartRatio = fadeInDuration / fadeOutDuration;\n\n fadeOutAction.warp( 1.0, startEndRatio, duration );\n this.warp( endStartRatio, 1.0, duration );\n\n }\n\n return this;\n\n },\n\n crossFadeTo: function ( fadeInAction, duration, warp ) {\n\n return fadeInAction.crossFadeFrom( this, duration, warp );\n\n },\n\n stopFading: function () {\n\n var weightInterpolant = this._weightInterpolant;\n\n if ( weightInterpolant !== null ) {\n\n this._weightInterpolant = null;\n this._mixer._takeBackControlInterpolant( weightInterpolant );\n\n }\n\n return this;\n\n },\n\n // Time Scale Control\n\n // set the time scale stopping any scheduled warping\n // although .paused = true yields an effective time scale of zero, this\n // method does *not* change .paused, because it would be confusing\n setEffectiveTimeScale: function ( timeScale ) {\n\n this.timeScale = timeScale;\n this._effectiveTimeScale = this.paused ? 0 : timeScale;\n\n return this.stopWarping();\n\n },\n\n // return the time scale considering warping and .paused\n getEffectiveTimeScale: function () {\n\n return this._effectiveTimeScale;\n\n },\n\n setDuration: function ( duration ) {\n\n this.timeScale = this._clip.duration / duration;\n\n return this.stopWarping();\n\n },\n\n syncWith: function ( action ) {\n\n this.time = action.time;\n this.timeScale = action.timeScale;\n\n return this.stopWarping();\n\n },\n\n halt: function ( duration ) {\n\n return this.warp( this._effectiveTimeScale, 0, duration );\n\n },\n\n warp: function ( startTimeScale, endTimeScale, duration ) {\n\n var mixer = this._mixer, now = mixer.time,\n interpolant = this._timeScaleInterpolant,\n\n timeScale = this.timeScale;\n\n if ( interpolant === null ) {\n\n interpolant = mixer._lendControlInterpolant();\n this._timeScaleInterpolant = interpolant;\n\n }\n\n var times = interpolant.parameterPositions,\n values = interpolant.sampleValues;\n\n times[ 0 ] = now;\n times[ 1 ] = now + duration;\n\n values[ 0 ] = startTimeScale / timeScale;\n values[ 1 ] = endTimeScale / timeScale;\n\n return this;\n\n },\n\n stopWarping: function () {\n\n var timeScaleInterpolant = this._timeScaleInterpolant;\n\n if ( timeScaleInterpolant !== null ) {\n\n this._timeScaleInterpolant = null;\n this._mixer._takeBackControlInterpolant( timeScaleInterpolant );\n\n }\n\n return this;\n\n },\n\n // Object Accessors\n\n getMixer: function () {\n\n return this._mixer;\n\n },\n\n getClip: function () {\n\n return this._clip;\n\n },\n\n getRoot: function () {\n\n return this._localRoot || this._mixer._root;\n\n },\n\n // Interna\n\n _update: function ( time, deltaTime, timeDirection, accuIndex ) {\n\n // called by the mixer\n\n if ( ! this.enabled ) {\n\n // call ._updateWeight() to update ._effectiveWeight\n\n this._updateWeight( time );\n return;\n\n }\n\n var startTime = this._startTime;\n\n if ( startTime !== null ) {\n\n // check for scheduled start of action\n\n var timeRunning = ( time - startTime ) * timeDirection;\n if ( timeRunning < 0 || timeDirection === 0 ) {\n\n return; // yet to come / don't decide when delta = 0\n\n }\n\n // start\n\n this._startTime = null; // unschedule\n deltaTime = timeDirection * timeRunning;\n\n }\n\n // apply time scale and advance time\n\n deltaTime *= this._updateTimeScale( time );\n var clipTime = this._updateTime( deltaTime );\n\n // note: _updateTime may disable the action resulting in\n // an effective weight of 0\n\n var weight = this._updateWeight( time );\n\n if ( weight > 0 ) {\n\n var interpolants = this._interpolants;\n var propertyMixers = this._propertyBindings;\n\n for ( var j = 0, m = interpolants.length; j !== m; ++ j ) {\n\n interpolants[ j ].evaluate( clipTime );\n propertyMixers[ j ].accumulate( accuIndex, weight );\n\n }\n\n }\n\n },\n\n _updateWeight: function ( time ) {\n\n var weight = 0;\n\n if ( this.enabled ) {\n\n weight = this.weight;\n var interpolant = this._weightInterpolant;\n\n if ( interpolant !== null ) {\n\n var interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n weight *= interpolantValue;\n\n if ( time > interpolant.parameterPositions[ 1 ] ) {\n\n this.stopFading();\n\n if ( interpolantValue === 0 ) {\n\n // faded out, disable\n this.enabled = false;\n\n }\n\n }\n\n }\n\n }\n\n this._effectiveWeight = weight;\n return weight;\n\n },\n\n _updateTimeScale: function ( time ) {\n\n var timeScale = 0;\n\n if ( ! this.paused ) {\n\n timeScale = this.timeScale;\n\n var interpolant = this._timeScaleInterpolant;\n\n if ( interpolant !== null ) {\n\n var interpolantValue = interpolant.evaluate( time )[ 0 ];\n\n timeScale *= interpolantValue;\n\n if ( time > interpolant.parameterPositions[ 1 ] ) {\n\n this.stopWarping();\n\n if ( timeScale === 0 ) {\n\n // motion has halted, pause\n this.paused = true;\n\n } else {\n\n // warp done - apply final time scale\n this.timeScale = timeScale;\n\n }\n\n }\n\n }\n\n }\n\n this._effectiveTimeScale = timeScale;\n return timeScale;\n\n },\n\n _updateTime: function ( deltaTime ) {\n\n var time = this.time + deltaTime;\n\n if ( deltaTime === 0 ) return time;\n\n var duration = this._clip.duration,\n\n loop = this.loop,\n loopCount = this._loopCount;\n\n if ( loop === LoopOnce ) {\n\n if ( loopCount === - 1 ) {\n\n // just started\n\n this._loopCount = 0;\n this._setEndings( true, true, false );\n\n }\n\n handle_stop: {\n\n if ( time >= duration ) {\n\n time = duration;\n\n } else if ( time < 0 ) {\n\n time = 0;\n\n } else break handle_stop;\n\n if ( this.clampWhenFinished ) this.paused = true;\n else this.enabled = false;\n\n this._mixer.dispatchEvent( {\n type: 'finished', action: this,\n direction: deltaTime < 0 ? - 1 : 1\n } );\n\n }\n\n } else { // repetitive Repeat or PingPong\n\n var pingPong = ( loop === LoopPingPong );\n\n if ( loopCount === - 1 ) {\n\n // just started\n\n if ( deltaTime >= 0 ) {\n\n loopCount = 0;\n\n this._setEndings( true, this.repetitions === 0, pingPong );\n\n } else {\n\n // when looping in reverse direction, the initial\n // transition through zero counts as a repetition,\n // so leave loopCount at -1\n\n this._setEndings( this.repetitions === 0, true, pingPong );\n\n }\n\n }\n\n if ( time >= duration || time < 0 ) {\n\n // wrap around\n\n var loopDelta = Math.floor( time / duration ); // signed\n time -= duration * loopDelta;\n\n loopCount += Math.abs( loopDelta );\n\n var pending = this.repetitions - loopCount;\n\n if ( pending <= 0 ) {\n\n // have to stop (switch state, clamp time, fire event)\n\n if ( this.clampWhenFinished ) this.paused = true;\n else this.enabled = false;\n\n time = deltaTime > 0 ? duration : 0;\n\n this._mixer.dispatchEvent( {\n type: 'finished', action: this,\n direction: deltaTime > 0 ? 1 : - 1\n } );\n\n } else {\n\n // keep running\n\n if ( pending === 1 ) {\n\n // entering the last round\n\n var atStart = deltaTime < 0;\n this._setEndings( atStart, ! atStart, pingPong );\n\n } else {\n\n this._setEndings( false, false, pingPong );\n\n }\n\n this._loopCount = loopCount;\n\n this._mixer.dispatchEvent( {\n type: 'loop', action: this, loopDelta: loopDelta\n } );\n\n }\n\n }\n\n if ( pingPong && ( loopCount & 1 ) === 1 ) {\n\n // invert time for the \"pong round\"\n\n this.time = time;\n return duration - time;\n\n }\n\n }\n\n this.time = time;\n return time;\n\n },\n\n _setEndings: function ( atStart, atEnd, pingPong ) {\n\n var settings = this._interpolantSettings;\n\n if ( pingPong ) {\n\n settings.endingStart = ZeroSlopeEnding;\n settings.endingEnd = ZeroSlopeEnding;\n\n } else {\n\n // assuming for LoopOnce atStart == atEnd == true\n\n if ( atStart ) {\n\n settings.endingStart = this.zeroSlopeAtStart ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n } else {\n\n settings.endingStart = WrapAroundEnding;\n\n }\n\n if ( atEnd ) {\n\n settings.endingEnd = this.zeroSlopeAtEnd ? ZeroSlopeEnding : ZeroCurvatureEnding;\n\n } else {\n\n settings.endingEnd = WrapAroundEnding;\n\n }\n\n }\n\n },\n\n _scheduleFading: function ( duration, weightNow, weightThen ) {\n\n var mixer = this._mixer, now = mixer.time,\n interpolant = this._weightInterpolant;\n\n if ( interpolant === null ) {\n\n interpolant = mixer._lendControlInterpolant();\n this._weightInterpolant = interpolant;\n\n }\n\n var times = interpolant.parameterPositions,\n values = interpolant.sampleValues;\n\n times[ 0 ] = now; values[ 0 ] = weightNow;\n times[ 1 ] = now + duration; values[ 1 ] = weightThen;\n\n return this;\n\n }\n\n } );\n\n /**\n *\n * Player for AnimationClips.\n *\n *\n * @author Ben Houston / http://clara.io/\n * @author David Sarno / http://lighthaus.us/\n * @author tschw\n */\n\n function AnimationMixer( root ) {\n\n this._root = root;\n this._initMemoryManager();\n this._accuIndex = 0;\n\n this.time = 0;\n\n this.timeScale = 1.0;\n\n }\n\n AnimationMixer.prototype = Object.assign( Object.create( EventDispatcher.prototype ), {\n\n constructor: AnimationMixer,\n\n _bindAction: function ( action, prototypeAction ) {\n\n var root = action._localRoot || this._root,\n tracks = action._clip.tracks,\n nTracks = tracks.length,\n bindings = action._propertyBindings,\n interpolants = action._interpolants,\n rootUuid = root.uuid,\n bindingsByRoot = this._bindingsByRootAndName,\n bindingsByName = bindingsByRoot[ rootUuid ];\n\n if ( bindingsByName === undefined ) {\n\n bindingsByName = {};\n bindingsByRoot[ rootUuid ] = bindingsByName;\n\n }\n\n for ( var i = 0; i !== nTracks; ++ i ) {\n\n var track = tracks[ i ],\n trackName = track.name,\n binding = bindingsByName[ trackName ];\n\n if ( binding !== undefined ) {\n\n bindings[ i ] = binding;\n\n } else {\n\n binding = bindings[ i ];\n\n if ( binding !== undefined ) {\n\n // existing binding, make sure the cache knows\n\n if ( binding._cacheIndex === null ) {\n\n ++ binding.referenceCount;\n this._addInactiveBinding( binding, rootUuid, trackName );\n\n }\n\n continue;\n\n }\n\n var path = prototypeAction && prototypeAction.\n _propertyBindings[ i ].binding.parsedPath;\n\n binding = new PropertyMixer(\n PropertyBinding.create( root, trackName, path ),\n track.ValueTypeName, track.getValueSize() );\n\n ++ binding.referenceCount;\n this._addInactiveBinding( binding, rootUuid, trackName );\n\n bindings[ i ] = binding;\n\n }\n\n interpolants[ i ].resultBuffer = binding.buffer;\n\n }\n\n },\n\n _activateAction: function ( action ) {\n\n if ( ! this._isActiveAction( action ) ) {\n\n if ( action._cacheIndex === null ) {\n\n // this action has been forgotten by the cache, but the user\n // appears to be still using it -> rebind\n\n var rootUuid = ( action._localRoot || this._root ).uuid,\n clipUuid = action._clip.uuid,\n actionsForClip = this._actionsByClip[ clipUuid ];\n\n this._bindAction( action,\n actionsForClip && actionsForClip.knownActions[ 0 ] );\n\n this._addInactiveAction( action, clipUuid, rootUuid );\n\n }\n\n var bindings = action._propertyBindings;\n\n // increment reference counts / sort out state\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( binding.useCount ++ === 0 ) {\n\n this._lendBinding( binding );\n binding.saveOriginalState();\n\n }\n\n }\n\n this._lendAction( action );\n\n }\n\n },\n\n _deactivateAction: function ( action ) {\n\n if ( this._isActiveAction( action ) ) {\n\n var bindings = action._propertyBindings;\n\n // decrement reference counts / sort out state\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( -- binding.useCount === 0 ) {\n\n binding.restoreOriginalState();\n this._takeBackBinding( binding );\n\n }\n\n }\n\n this._takeBackAction( action );\n\n }\n\n },\n\n // Memory manager\n\n _initMemoryManager: function () {\n\n this._actions = []; // 'nActiveActions' followed by inactive ones\n this._nActiveActions = 0;\n\n this._actionsByClip = {};\n // inside:\n // {\n // knownActions: Array< AnimationAction > - used as prototypes\n // actionByRoot: AnimationAction - lookup\n // }\n\n\n this._bindings = []; // 'nActiveBindings' followed by inactive ones\n this._nActiveBindings = 0;\n\n this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer >\n\n\n this._controlInterpolants = []; // same game as above\n this._nActiveControlInterpolants = 0;\n\n var scope = this;\n\n this.stats = {\n\n actions: {\n get total() {\n\n return scope._actions.length;\n\n },\n get inUse() {\n\n return scope._nActiveActions;\n\n }\n },\n bindings: {\n get total() {\n\n return scope._bindings.length;\n\n },\n get inUse() {\n\n return scope._nActiveBindings;\n\n }\n },\n controlInterpolants: {\n get total() {\n\n return scope._controlInterpolants.length;\n\n },\n get inUse() {\n\n return scope._nActiveControlInterpolants;\n\n }\n }\n\n };\n\n },\n\n // Memory management for AnimationAction objects\n\n _isActiveAction: function ( action ) {\n\n var index = action._cacheIndex;\n return index !== null && index < this._nActiveActions;\n\n },\n\n _addInactiveAction: function ( action, clipUuid, rootUuid ) {\n\n var actions = this._actions,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ];\n\n if ( actionsForClip === undefined ) {\n\n actionsForClip = {\n\n knownActions: [ action ],\n actionByRoot: {}\n\n };\n\n action._byClipCacheIndex = 0;\n\n actionsByClip[ clipUuid ] = actionsForClip;\n\n } else {\n\n var knownActions = actionsForClip.knownActions;\n\n action._byClipCacheIndex = knownActions.length;\n knownActions.push( action );\n\n }\n\n action._cacheIndex = actions.length;\n actions.push( action );\n\n actionsForClip.actionByRoot[ rootUuid ] = action;\n\n },\n\n _removeInactiveAction: function ( action ) {\n\n var actions = this._actions,\n lastInactiveAction = actions[ actions.length - 1 ],\n cacheIndex = action._cacheIndex;\n\n lastInactiveAction._cacheIndex = cacheIndex;\n actions[ cacheIndex ] = lastInactiveAction;\n actions.pop();\n\n action._cacheIndex = null;\n\n\n var clipUuid = action._clip.uuid,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ],\n knownActionsForClip = actionsForClip.knownActions,\n\n lastKnownAction =\n knownActionsForClip[ knownActionsForClip.length - 1 ],\n\n byClipCacheIndex = action._byClipCacheIndex;\n\n lastKnownAction._byClipCacheIndex = byClipCacheIndex;\n knownActionsForClip[ byClipCacheIndex ] = lastKnownAction;\n knownActionsForClip.pop();\n\n action._byClipCacheIndex = null;\n\n\n var actionByRoot = actionsForClip.actionByRoot,\n rootUuid = ( action._localRoot || this._root ).uuid;\n\n delete actionByRoot[ rootUuid ];\n\n if ( knownActionsForClip.length === 0 ) {\n\n delete actionsByClip[ clipUuid ];\n\n }\n\n this._removeInactiveBindingsForAction( action );\n\n },\n\n _removeInactiveBindingsForAction: function ( action ) {\n\n var bindings = action._propertyBindings;\n for ( var i = 0, n = bindings.length; i !== n; ++ i ) {\n\n var binding = bindings[ i ];\n\n if ( -- binding.referenceCount === 0 ) {\n\n this._removeInactiveBinding( binding );\n\n }\n\n }\n\n },\n\n _lendAction: function ( action ) {\n\n // [ active actions | inactive actions ]\n // [ active actions >| inactive actions ]\n // s a\n // <-swap->\n // a s\n\n var actions = this._actions,\n prevIndex = action._cacheIndex,\n\n lastActiveIndex = this._nActiveActions ++,\n\n firstInactiveAction = actions[ lastActiveIndex ];\n\n action._cacheIndex = lastActiveIndex;\n actions[ lastActiveIndex ] = action;\n\n firstInactiveAction._cacheIndex = prevIndex;\n actions[ prevIndex ] = firstInactiveAction;\n\n },\n\n _takeBackAction: function ( action ) {\n\n // [ active actions | inactive actions ]\n // [ active actions |< inactive actions ]\n // a s\n // <-swap->\n // s a\n\n var actions = this._actions,\n prevIndex = action._cacheIndex,\n\n firstInactiveIndex = -- this._nActiveActions,\n\n lastActiveAction = actions[ firstInactiveIndex ];\n\n action._cacheIndex = firstInactiveIndex;\n actions[ firstInactiveIndex ] = action;\n\n lastActiveAction._cacheIndex = prevIndex;\n actions[ prevIndex ] = lastActiveAction;\n\n },\n\n // Memory management for PropertyMixer objects\n\n _addInactiveBinding: function ( binding, rootUuid, trackName ) {\n\n var bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ],\n\n bindings = this._bindings;\n\n if ( bindingByName === undefined ) {\n\n bindingByName = {};\n bindingsByRoot[ rootUuid ] = bindingByName;\n\n }\n\n bindingByName[ trackName ] = binding;\n\n binding._cacheIndex = bindings.length;\n bindings.push( binding );\n\n },\n\n _removeInactiveBinding: function ( binding ) {\n\n var bindings = this._bindings,\n propBinding = binding.binding,\n rootUuid = propBinding.rootNode.uuid,\n trackName = propBinding.path,\n bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ],\n\n lastInactiveBinding = bindings[ bindings.length - 1 ],\n cacheIndex = binding._cacheIndex;\n\n lastInactiveBinding._cacheIndex = cacheIndex;\n bindings[ cacheIndex ] = lastInactiveBinding;\n bindings.pop();\n\n delete bindingByName[ trackName ];\n\n remove_empty_map: {\n\n for ( var _ in bindingByName ) break remove_empty_map; // eslint-disable-line no-unused-vars\n\n delete bindingsByRoot[ rootUuid ];\n\n }\n\n },\n\n _lendBinding: function ( binding ) {\n\n var bindings = this._bindings,\n prevIndex = binding._cacheIndex,\n\n lastActiveIndex = this._nActiveBindings ++,\n\n firstInactiveBinding = bindings[ lastActiveIndex ];\n\n binding._cacheIndex = lastActiveIndex;\n bindings[ lastActiveIndex ] = binding;\n\n firstInactiveBinding._cacheIndex = prevIndex;\n bindings[ prevIndex ] = firstInactiveBinding;\n\n },\n\n _takeBackBinding: function ( binding ) {\n\n var bindings = this._bindings,\n prevIndex = binding._cacheIndex,\n\n firstInactiveIndex = -- this._nActiveBindings,\n\n lastActiveBinding = bindings[ firstInactiveIndex ];\n\n binding._cacheIndex = firstInactiveIndex;\n bindings[ firstInactiveIndex ] = binding;\n\n lastActiveBinding._cacheIndex = prevIndex;\n bindings[ prevIndex ] = lastActiveBinding;\n\n },\n\n\n // Memory management of Interpolants for weight and time scale\n\n _lendControlInterpolant: function () {\n\n var interpolants = this._controlInterpolants,\n lastActiveIndex = this._nActiveControlInterpolants ++,\n interpolant = interpolants[ lastActiveIndex ];\n\n if ( interpolant === undefined ) {\n\n interpolant = new LinearInterpolant(\n new Float32Array( 2 ), new Float32Array( 2 ),\n 1, this._controlInterpolantsResultBuffer );\n\n interpolant.__cacheIndex = lastActiveIndex;\n interpolants[ lastActiveIndex ] = interpolant;\n\n }\n\n return interpolant;\n\n },\n\n _takeBackControlInterpolant: function ( interpolant ) {\n\n var interpolants = this._controlInterpolants,\n prevIndex = interpolant.__cacheIndex,\n\n firstInactiveIndex = -- this._nActiveControlInterpolants,\n\n lastActiveInterpolant = interpolants[ firstInactiveIndex ];\n\n interpolant.__cacheIndex = firstInactiveIndex;\n interpolants[ firstInactiveIndex ] = interpolant;\n\n lastActiveInterpolant.__cacheIndex = prevIndex;\n interpolants[ prevIndex ] = lastActiveInterpolant;\n\n },\n\n _controlInterpolantsResultBuffer: new Float32Array( 1 ),\n\n // return an action for a clip optionally using a custom root target\n // object (this method allocates a lot of dynamic memory in case a\n // previously unknown clip/root combination is specified)\n clipAction: function ( clip, optionalRoot ) {\n\n var root = optionalRoot || this._root,\n rootUuid = root.uuid,\n\n clipObject = typeof clip === 'string' ?\n AnimationClip.findByName( root, clip ) : clip,\n\n clipUuid = clipObject !== null ? clipObject.uuid : clip,\n\n actionsForClip = this._actionsByClip[ clipUuid ],\n prototypeAction = null;\n\n if ( actionsForClip !== undefined ) {\n\n var existingAction =\n actionsForClip.actionByRoot[ rootUuid ];\n\n if ( existingAction !== undefined ) {\n\n return existingAction;\n\n }\n\n // we know the clip, so we don't have to parse all\n // the bindings again but can just copy\n prototypeAction = actionsForClip.knownActions[ 0 ];\n\n // also, take the clip from the prototype action\n if ( clipObject === null )\n clipObject = prototypeAction._clip;\n\n }\n\n // clip must be known when specified via string\n if ( clipObject === null ) return null;\n\n // allocate all resources required to run it\n var newAction = new AnimationAction( this, clipObject, optionalRoot );\n\n this._bindAction( newAction, prototypeAction );\n\n // and make the action known to the memory manager\n this._addInactiveAction( newAction, clipUuid, rootUuid );\n\n return newAction;\n\n },\n\n // get an existing action\n existingAction: function ( clip, optionalRoot ) {\n\n var root = optionalRoot || this._root,\n rootUuid = root.uuid,\n\n clipObject = typeof clip === 'string' ?\n AnimationClip.findByName( root, clip ) : clip,\n\n clipUuid = clipObject ? clipObject.uuid : clip,\n\n actionsForClip = this._actionsByClip[ clipUuid ];\n\n if ( actionsForClip !== undefined ) {\n\n return actionsForClip.actionByRoot[ rootUuid ] || null;\n\n }\n\n return null;\n\n },\n\n // deactivates all previously scheduled actions\n stopAllAction: function () {\n\n var actions = this._actions,\n nActions = this._nActiveActions,\n bindings = this._bindings,\n nBindings = this._nActiveBindings;\n\n this._nActiveActions = 0;\n this._nActiveBindings = 0;\n\n for ( var i = 0; i !== nActions; ++ i ) {\n\n actions[ i ].reset();\n\n }\n\n for ( var i = 0; i !== nBindings; ++ i ) {\n\n bindings[ i ].useCount = 0;\n\n }\n\n return this;\n\n },\n\n // advance the time and update apply the animation\n update: function ( deltaTime ) {\n\n deltaTime *= this.timeScale;\n\n var actions = this._actions,\n nActions = this._nActiveActions,\n\n time = this.time += deltaTime,\n timeDirection = Math.sign( deltaTime ),\n\n accuIndex = this._accuIndex ^= 1;\n\n // run active actions\n\n for ( var i = 0; i !== nActions; ++ i ) {\n\n var action = actions[ i ];\n\n action._update( time, deltaTime, timeDirection, accuIndex );\n\n }\n\n // update scene graph\n\n var bindings = this._bindings,\n nBindings = this._nActiveBindings;\n\n for ( var i = 0; i !== nBindings; ++ i ) {\n\n bindings[ i ].apply( accuIndex );\n\n }\n\n return this;\n\n },\n\n // return this mixer's root target object\n getRoot: function () {\n\n return this._root;\n\n },\n\n // free all resources specific to a particular clip\n uncacheClip: function ( clip ) {\n\n var actions = this._actions,\n clipUuid = clip.uuid,\n actionsByClip = this._actionsByClip,\n actionsForClip = actionsByClip[ clipUuid ];\n\n if ( actionsForClip !== undefined ) {\n\n // note: just calling _removeInactiveAction would mess up the\n // iteration state and also require updating the state we can\n // just throw away\n\n var actionsToRemove = actionsForClip.knownActions;\n\n for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) {\n\n var action = actionsToRemove[ i ];\n\n this._deactivateAction( action );\n\n var cacheIndex = action._cacheIndex,\n lastInactiveAction = actions[ actions.length - 1 ];\n\n action._cacheIndex = null;\n action._byClipCacheIndex = null;\n\n lastInactiveAction._cacheIndex = cacheIndex;\n actions[ cacheIndex ] = lastInactiveAction;\n actions.pop();\n\n this._removeInactiveBindingsForAction( action );\n\n }\n\n delete actionsByClip[ clipUuid ];\n\n }\n\n },\n\n // free all resources specific to a particular root target object\n uncacheRoot: function ( root ) {\n\n var rootUuid = root.uuid,\n actionsByClip = this._actionsByClip;\n\n for ( var clipUuid in actionsByClip ) {\n\n var actionByRoot = actionsByClip[ clipUuid ].actionByRoot,\n action = actionByRoot[ rootUuid ];\n\n if ( action !== undefined ) {\n\n this._deactivateAction( action );\n this._removeInactiveAction( action );\n\n }\n\n }\n\n var bindingsByRoot = this._bindingsByRootAndName,\n bindingByName = bindingsByRoot[ rootUuid ];\n\n if ( bindingByName !== undefined ) {\n\n for ( var trackName in bindingByName ) {\n\n var binding = bindingByName[ trackName ];\n binding.restoreOriginalState();\n this._removeInactiveBinding( binding );\n\n }\n\n }\n\n },\n\n // remove a targeted clip from the cache\n uncacheAction: function ( clip, optionalRoot ) {\n\n var action = this.existingAction( clip, optionalRoot );\n\n if ( action !== null ) {\n\n this._deactivateAction( action );\n this._removeInactiveAction( action );\n\n }\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Uniform( value ) {\n\n if ( typeof value === 'string' ) {\n\n console.warn( 'THREE.Uniform: Type parameter is no longer needed.' );\n value = arguments[ 1 ];\n\n }\n\n this.value = value;\n\n }\n\n Uniform.prototype.clone = function () {\n\n return new Uniform( this.value.clone === undefined ? this.value : this.value.clone() );\n\n };\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedBufferGeometry() {\n\n BufferGeometry.call( this );\n\n this.type = 'InstancedBufferGeometry';\n this.maxInstancedCount = undefined;\n\n }\n\n InstancedBufferGeometry.prototype = Object.assign( Object.create( BufferGeometry.prototype ), {\n\n constructor: InstancedBufferGeometry,\n\n isInstancedBufferGeometry: true,\n\n copy: function ( source ) {\n\n BufferGeometry.prototype.copy.call( this, source );\n\n this.maxInstancedCount = source.maxInstancedCount;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InterleavedBufferAttribute( interleavedBuffer, itemSize, offset, normalized ) {\n\n this.data = interleavedBuffer;\n this.itemSize = itemSize;\n this.offset = offset;\n\n this.normalized = normalized === true;\n\n }\n\n Object.defineProperties( InterleavedBufferAttribute.prototype, {\n\n count: {\n\n get: function () {\n\n return this.data.count;\n\n }\n\n },\n\n array: {\n\n get: function () {\n\n return this.data.array;\n\n }\n\n }\n\n } );\n\n Object.assign( InterleavedBufferAttribute.prototype, {\n\n isInterleavedBufferAttribute: true,\n\n setX: function ( index, x ) {\n\n this.data.array[ index * this.data.stride + this.offset ] = x;\n\n return this;\n\n },\n\n setY: function ( index, y ) {\n\n this.data.array[ index * this.data.stride + this.offset + 1 ] = y;\n\n return this;\n\n },\n\n setZ: function ( index, z ) {\n\n this.data.array[ index * this.data.stride + this.offset + 2 ] = z;\n\n return this;\n\n },\n\n setW: function ( index, w ) {\n\n this.data.array[ index * this.data.stride + this.offset + 3 ] = w;\n\n return this;\n\n },\n\n getX: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset ];\n\n },\n\n getY: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 1 ];\n\n },\n\n getZ: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 2 ];\n\n },\n\n getW: function ( index ) {\n\n return this.data.array[ index * this.data.stride + this.offset + 3 ];\n\n },\n\n setXY: function ( index, x, y ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n\n return this;\n\n },\n\n setXYZ: function ( index, x, y, z ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n this.data.array[ index + 2 ] = z;\n\n return this;\n\n },\n\n setXYZW: function ( index, x, y, z, w ) {\n\n index = index * this.data.stride + this.offset;\n\n this.data.array[ index + 0 ] = x;\n this.data.array[ index + 1 ] = y;\n this.data.array[ index + 2 ] = z;\n this.data.array[ index + 3 ] = w;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InterleavedBuffer( array, stride ) {\n\n this.array = array;\n this.stride = stride;\n this.count = array !== undefined ? array.length / stride : 0;\n\n this.dynamic = false;\n this.updateRange = { offset: 0, count: - 1 };\n\n this.version = 0;\n\n }\n\n Object.defineProperty( InterleavedBuffer.prototype, 'needsUpdate', {\n\n set: function ( value ) {\n\n if ( value === true ) this.version ++;\n\n }\n\n } );\n\n Object.assign( InterleavedBuffer.prototype, {\n\n isInterleavedBuffer: true,\n\n onUploadCallback: function () {},\n\n setArray: function ( array ) {\n\n if ( Array.isArray( array ) ) {\n\n throw new TypeError( 'THREE.BufferAttribute: array should be a Typed Array.' );\n\n }\n\n this.count = array !== undefined ? array.length / this.stride : 0;\n this.array = array;\n\n },\n\n setDynamic: function ( value ) {\n\n this.dynamic = value;\n\n return this;\n\n },\n\n copy: function ( source ) {\n\n this.array = new source.array.constructor( source.array );\n this.count = source.count;\n this.stride = source.stride;\n this.dynamic = source.dynamic;\n\n return this;\n\n },\n\n copyAt: function ( index1, attribute, index2 ) {\n\n index1 *= this.stride;\n index2 *= attribute.stride;\n\n for ( var i = 0, l = this.stride; i < l; i ++ ) {\n\n this.array[ index1 + i ] = attribute.array[ index2 + i ];\n\n }\n\n return this;\n\n },\n\n set: function ( value, offset ) {\n\n if ( offset === undefined ) offset = 0;\n\n this.array.set( value, offset );\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n onUpload: function ( callback ) {\n\n this.onUploadCallback = callback;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedInterleavedBuffer( array, stride, meshPerAttribute ) {\n\n InterleavedBuffer.call( this, array, stride );\n\n this.meshPerAttribute = meshPerAttribute || 1;\n\n }\n\n InstancedInterleavedBuffer.prototype = Object.assign( Object.create( InterleavedBuffer.prototype ), {\n\n constructor: InstancedInterleavedBuffer,\n\n isInstancedInterleavedBuffer: true,\n\n copy: function ( source ) {\n\n InterleavedBuffer.prototype.copy.call( this, source );\n\n this.meshPerAttribute = source.meshPerAttribute;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author benaadams / https://twitter.com/ben_a_adams\n */\n\n function InstancedBufferAttribute( array, itemSize, meshPerAttribute ) {\n\n BufferAttribute.call( this, array, itemSize );\n\n this.meshPerAttribute = meshPerAttribute || 1;\n\n }\n\n InstancedBufferAttribute.prototype = Object.assign( Object.create( BufferAttribute.prototype ), {\n\n constructor: InstancedBufferAttribute,\n\n isInstancedBufferAttribute: true,\n\n copy: function ( source ) {\n\n BufferAttribute.prototype.copy.call( this, source );\n\n this.meshPerAttribute = source.meshPerAttribute;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author bhouston / http://clara.io/\n * @author stephomi / http://stephaneginier.com/\n */\n\n function Raycaster( origin, direction, near, far ) {\n\n this.ray = new Ray( origin, direction );\n // direction is assumed to be normalized (for accurate distance calculations)\n\n this.near = near || 0;\n this.far = far || Infinity;\n\n this.params = {\n Mesh: {},\n Line: {},\n LOD: {},\n Points: { threshold: 1 },\n Sprite: {}\n };\n\n Object.defineProperties( this.params, {\n PointCloud: {\n get: function () {\n\n console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' );\n return this.Points;\n\n }\n }\n } );\n\n }\n\n function ascSort( a, b ) {\n\n return a.distance - b.distance;\n\n }\n\n function intersectObject( object, raycaster, intersects, recursive ) {\n\n if ( object.visible === false ) return;\n\n object.raycast( raycaster, intersects );\n\n if ( recursive === true ) {\n\n var children = object.children;\n\n for ( var i = 0, l = children.length; i < l; i ++ ) {\n\n intersectObject( children[ i ], raycaster, intersects, true );\n\n }\n\n }\n\n }\n\n Object.assign( Raycaster.prototype, {\n\n linePrecision: 1,\n\n set: function ( origin, direction ) {\n\n // direction is assumed to be normalized (for accurate distance calculations)\n\n this.ray.set( origin, direction );\n\n },\n\n setFromCamera: function ( coords, camera ) {\n\n if ( ( camera && camera.isPerspectiveCamera ) ) {\n\n this.ray.origin.setFromMatrixPosition( camera.matrixWorld );\n this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize();\n\n } else if ( ( camera && camera.isOrthographicCamera ) ) {\n\n this.ray.origin.set( coords.x, coords.y, ( camera.near + camera.far ) / ( camera.near - camera.far ) ).unproject( camera ); // set origin in plane of camera\n this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld );\n\n } else {\n\n console.error( 'THREE.Raycaster: Unsupported camera type.' );\n\n }\n\n },\n\n intersectObject: function ( object, recursive, optionalTarget ) {\n\n var intersects = optionalTarget || [];\n\n intersectObject( object, this, intersects, recursive );\n\n intersects.sort( ascSort );\n\n return intersects;\n\n },\n\n intersectObjects: function ( objects, recursive, optionalTarget ) {\n\n var intersects = optionalTarget || [];\n\n if ( Array.isArray( objects ) === false ) {\n\n console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' );\n return intersects;\n\n }\n\n for ( var i = 0, l = objects.length; i < l; i ++ ) {\n\n intersectObject( objects[ i ], this, intersects, recursive );\n\n }\n\n intersects.sort( ascSort );\n\n return intersects;\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function Clock( autoStart ) {\n\n this.autoStart = ( autoStart !== undefined ) ? autoStart : true;\n\n this.startTime = 0;\n this.oldTime = 0;\n this.elapsedTime = 0;\n\n this.running = false;\n\n }\n\n Object.assign( Clock.prototype, {\n\n start: function () {\n\n this.startTime = ( typeof performance === 'undefined' ? Date : performance ).now(); // see #10732\n\n this.oldTime = this.startTime;\n this.elapsedTime = 0;\n this.running = true;\n\n },\n\n stop: function () {\n\n this.getElapsedTime();\n this.running = false;\n this.autoStart = false;\n\n },\n\n getElapsedTime: function () {\n\n this.getDelta();\n return this.elapsedTime;\n\n },\n\n getDelta: function () {\n\n var diff = 0;\n\n if ( this.autoStart && ! this.running ) {\n\n this.start();\n return 0;\n\n }\n\n if ( this.running ) {\n\n var newTime = ( typeof performance === 'undefined' ? Date : performance ).now();\n\n diff = ( newTime - this.oldTime ) / 1000;\n this.oldTime = newTime;\n\n this.elapsedTime += diff;\n\n }\n\n return diff;\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n * @author WestLangley / http://github.com/WestLangley\n *\n * Ref: https://en.wikipedia.org/wiki/Spherical_coordinate_system\n *\n * The poles (phi) are at the positive and negative y axis.\n * The equator starts at positive z.\n */\n\n function Spherical( radius, phi, theta ) {\n\n this.radius = ( radius !== undefined ) ? radius : 1.0;\n this.phi = ( phi !== undefined ) ? phi : 0; // up / down towards top and bottom pole\n this.theta = ( theta !== undefined ) ? theta : 0; // around the equator of the sphere\n\n return this;\n\n }\n\n Object.assign( Spherical.prototype, {\n\n set: function ( radius, phi, theta ) {\n\n this.radius = radius;\n this.phi = phi;\n this.theta = theta;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( other ) {\n\n this.radius = other.radius;\n this.phi = other.phi;\n this.theta = other.theta;\n\n return this;\n\n },\n\n // restrict phi to be betwee EPS and PI-EPS\n makeSafe: function () {\n\n var EPS = 0.000001;\n this.phi = Math.max( EPS, Math.min( Math.PI - EPS, this.phi ) );\n\n return this;\n\n },\n\n setFromVector3: function ( vec3 ) {\n\n this.radius = vec3.length();\n\n if ( this.radius === 0 ) {\n\n this.theta = 0;\n this.phi = 0;\n\n } else {\n\n this.theta = Math.atan2( vec3.x, vec3.z ); // equator angle around y-up axis\n this.phi = Math.acos( _Math.clamp( vec3.y / this.radius, - 1, 1 ) ); // polar angle\n\n }\n\n return this;\n\n }\n\n } );\n\n /**\n * @author Mugen87 / https://github.com/Mugen87\n *\n * Ref: https://en.wikipedia.org/wiki/Cylindrical_coordinate_system\n *\n */\n\n function Cylindrical( radius, theta, y ) {\n\n this.radius = ( radius !== undefined ) ? radius : 1.0; // distance from the origin to a point in the x-z plane\n this.theta = ( theta !== undefined ) ? theta : 0; // counterclockwise angle in the x-z plane measured in radians from the positive z-axis\n this.y = ( y !== undefined ) ? y : 0; // height above the x-z plane\n\n return this;\n\n }\n\n Object.assign( Cylindrical.prototype, {\n\n set: function ( radius, theta, y ) {\n\n this.radius = radius;\n this.theta = theta;\n this.y = y;\n\n return this;\n\n },\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( other ) {\n\n this.radius = other.radius;\n this.theta = other.theta;\n this.y = other.y;\n\n return this;\n\n },\n\n setFromVector3: function ( vec3 ) {\n\n this.radius = Math.sqrt( vec3.x * vec3.x + vec3.z * vec3.z );\n this.theta = Math.atan2( vec3.x, vec3.z );\n this.y = vec3.y;\n\n return this;\n\n }\n\n } );\n\n /**\n * @author bhouston / http://clara.io\n */\n\n function Box2( min, max ) {\n\n this.min = ( min !== undefined ) ? min : new Vector2( + Infinity, + Infinity );\n this.max = ( max !== undefined ) ? max : new Vector2( - Infinity, - Infinity );\n\n }\n\n Object.assign( Box2.prototype, {\n\n set: function ( min, max ) {\n\n this.min.copy( min );\n this.max.copy( max );\n\n return this;\n\n },\n\n setFromPoints: function ( points ) {\n\n this.makeEmpty();\n\n for ( var i = 0, il = points.length; i < il; i ++ ) {\n\n this.expandByPoint( points[ i ] );\n\n }\n\n return this;\n\n },\n\n setFromCenterAndSize: function () {\n\n var v1 = new Vector2();\n\n return function setFromCenterAndSize( center, size ) {\n\n var halfSize = v1.copy( size ).multiplyScalar( 0.5 );\n this.min.copy( center ).sub( halfSize );\n this.max.copy( center ).add( halfSize );\n\n return this;\n\n };\n\n }(),\n\n clone: function () {\n\n return new this.constructor().copy( this );\n\n },\n\n copy: function ( box ) {\n\n this.min.copy( box.min );\n this.max.copy( box.max );\n\n return this;\n\n },\n\n makeEmpty: function () {\n\n this.min.x = this.min.y = + Infinity;\n this.max.x = this.max.y = - Infinity;\n\n return this;\n\n },\n\n isEmpty: function () {\n\n // this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes\n\n return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y );\n\n },\n\n getCenter: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getCenter() target is now required' );\n target = new Vector2();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0 ) : target.addVectors( this.min, this.max ).multiplyScalar( 0.5 );\n\n },\n\n getSize: function ( target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getSize() target is now required' );\n target = new Vector2();\n\n }\n\n return this.isEmpty() ? target.set( 0, 0 ) : target.subVectors( this.max, this.min );\n\n },\n\n expandByPoint: function ( point ) {\n\n this.min.min( point );\n this.max.max( point );\n\n return this;\n\n },\n\n expandByVector: function ( vector ) {\n\n this.min.sub( vector );\n this.max.add( vector );\n\n return this;\n\n },\n\n expandByScalar: function ( scalar ) {\n\n this.min.addScalar( - scalar );\n this.max.addScalar( scalar );\n\n return this;\n\n },\n\n containsPoint: function ( point ) {\n\n return point.x < this.min.x || point.x > this.max.x ||\n point.y < this.min.y || point.y > this.max.y ? false : true;\n\n },\n\n containsBox: function ( box ) {\n\n return this.min.x <= box.min.x && box.max.x <= this.max.x &&\n this.min.y <= box.min.y && box.max.y <= this.max.y;\n\n },\n\n getParameter: function ( point, target ) {\n\n // This can potentially have a divide by zero if the box\n // has a size dimension of 0.\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .getParameter() target is now required' );\n target = new Vector2();\n\n }\n\n return target.set(\n ( point.x - this.min.x ) / ( this.max.x - this.min.x ),\n ( point.y - this.min.y ) / ( this.max.y - this.min.y )\n );\n\n },\n\n intersectsBox: function ( box ) {\n\n // using 4 splitting planes to rule out intersections\n\n return box.max.x < this.min.x || box.min.x > this.max.x ||\n box.max.y < this.min.y || box.min.y > this.max.y ? false : true;\n\n },\n\n clampPoint: function ( point, target ) {\n\n if ( target === undefined ) {\n\n console.warn( 'THREE.Box2: .clampPoint() target is now required' );\n target = new Vector2();\n\n }\n\n return target.copy( point ).clamp( this.min, this.max );\n\n },\n\n distanceToPoint: function () {\n\n var v1 = new Vector2();\n\n return function distanceToPoint( point ) {\n\n var clampedPoint = v1.copy( point ).clamp( this.min, this.max );\n return clampedPoint.sub( point ).length();\n\n };\n\n }(),\n\n intersect: function ( box ) {\n\n this.min.max( box.min );\n this.max.min( box.max );\n\n return this;\n\n },\n\n union: function ( box ) {\n\n this.min.min( box.min );\n this.max.max( box.max );\n\n return this;\n\n },\n\n translate: function ( offset ) {\n\n this.min.add( offset );\n this.max.add( offset );\n\n return this;\n\n },\n\n equals: function ( box ) {\n\n return box.min.equals( this.min ) && box.max.equals( this.max );\n\n }\n\n } );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n */\n\n function ImmediateRenderObject( material ) {\n\n Object3D.call( this );\n\n this.material = material;\n this.render = function ( /* renderCallback */ ) {};\n\n }\n\n ImmediateRenderObject.prototype = Object.create( Object3D.prototype );\n ImmediateRenderObject.prototype.constructor = ImmediateRenderObject;\n\n ImmediateRenderObject.prototype.isImmediateRenderObject = true;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function VertexNormalsHelper( object, size, hex, linewidth ) {\n\n this.object = object;\n\n this.size = ( size !== undefined ) ? size : 1;\n\n var color = ( hex !== undefined ) ? hex : 0xff0000;\n\n var width = ( linewidth !== undefined ) ? linewidth : 1;\n\n //\n\n var nNormals = 0;\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n nNormals = objGeometry.faces.length * 3;\n\n } else if ( objGeometry && objGeometry.isBufferGeometry ) {\n\n nNormals = objGeometry.attributes.normal.count;\n\n }\n\n //\n\n var geometry = new BufferGeometry();\n\n var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );\n\n geometry.addAttribute( 'position', positions );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );\n\n //\n\n this.matrixAutoUpdate = false;\n\n this.update();\n\n }\n\n VertexNormalsHelper.prototype = Object.create( LineSegments.prototype );\n VertexNormalsHelper.prototype.constructor = VertexNormalsHelper;\n\n VertexNormalsHelper.prototype.update = ( function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var normalMatrix = new Matrix3();\n\n return function update() {\n\n var keys = [ 'a', 'b', 'c' ];\n\n this.object.updateMatrixWorld( true );\n\n normalMatrix.getNormalMatrix( this.object.matrixWorld );\n\n var matrixWorld = this.object.matrixWorld;\n\n var position = this.geometry.attributes.position;\n\n //\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n var vertices = objGeometry.vertices;\n\n var faces = objGeometry.faces;\n\n var idx = 0;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) {\n\n var vertex = vertices[ face[ keys[ j ] ] ];\n\n var normal = face.vertexNormals[ j ];\n\n v1.copy( vertex ).applyMatrix4( matrixWorld );\n\n v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n }\n\n } else if ( objGeometry && objGeometry.isBufferGeometry ) {\n\n var objPos = objGeometry.attributes.position;\n\n var objNorm = objGeometry.attributes.normal;\n\n var idx = 0;\n\n // for simplicity, ignore index and drawcalls, and render every normal\n\n for ( var j = 0, jl = objPos.count; j < jl; j ++ ) {\n\n v1.set( objPos.getX( j ), objPos.getY( j ), objPos.getZ( j ) ).applyMatrix4( matrixWorld );\n\n v2.set( objNorm.getX( j ), objNorm.getY( j ), objNorm.getZ( j ) );\n\n v2.applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n }\n\n position.needsUpdate = true;\n\n };\n\n }() );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function SpotLightHelper( light, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var geometry = new BufferGeometry();\n\n var positions = [\n 0, 0, 0, 0, 0, 1,\n 0, 0, 0, 1, 0, 1,\n 0, 0, 0, - 1, 0, 1,\n 0, 0, 0, 0, 1, 1,\n 0, 0, 0, 0, - 1, 1\n ];\n\n for ( var i = 0, j = 1, l = 32; i < l; i ++, j ++ ) {\n\n var p1 = ( i / l ) * Math.PI * 2;\n var p2 = ( j / l ) * Math.PI * 2;\n\n positions.push(\n Math.cos( p1 ), Math.sin( p1 ), 1,\n Math.cos( p2 ), Math.sin( p2 ), 1\n );\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n var material = new LineBasicMaterial( { fog: false } );\n\n this.cone = new LineSegments( geometry, material );\n this.add( this.cone );\n\n this.update();\n\n }\n\n SpotLightHelper.prototype = Object.create( Object3D.prototype );\n SpotLightHelper.prototype.constructor = SpotLightHelper;\n\n SpotLightHelper.prototype.dispose = function () {\n\n this.cone.geometry.dispose();\n this.cone.material.dispose();\n\n };\n\n SpotLightHelper.prototype.update = function () {\n\n var vector = new Vector3();\n var vector2 = new Vector3();\n\n return function update() {\n\n this.light.updateMatrixWorld();\n\n var coneLength = this.light.distance ? this.light.distance : 1000;\n var coneWidth = coneLength * Math.tan( this.light.angle );\n\n this.cone.scale.set( coneWidth, coneWidth, coneLength );\n\n vector.setFromMatrixPosition( this.light.matrixWorld );\n vector2.setFromMatrixPosition( this.light.target.matrixWorld );\n\n this.cone.lookAt( vector2.sub( vector ) );\n\n if ( this.color !== undefined ) {\n\n this.cone.material.color.set( this.color );\n\n } else {\n\n this.cone.material.color.copy( this.light.color );\n\n }\n\n };\n\n }();\n\n /**\n * @author Sean Griffin / http://twitter.com/sgrif\n * @author Michael Guerrero / http://realitymeltdown.com\n * @author mrdoob / http://mrdoob.com/\n * @author ikerr / http://verold.com\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function getBoneList( object ) {\n\n var boneList = [];\n\n if ( object && object.isBone ) {\n\n boneList.push( object );\n\n }\n\n for ( var i = 0; i < object.children.length; i ++ ) {\n\n boneList.push.apply( boneList, getBoneList( object.children[ i ] ) );\n\n }\n\n return boneList;\n\n }\n\n function SkeletonHelper( object ) {\n\n var bones = getBoneList( object );\n\n var geometry = new BufferGeometry();\n\n var vertices = [];\n var colors = [];\n\n var color1 = new Color( 0, 0, 1 );\n var color2 = new Color( 0, 1, 0 );\n\n for ( var i = 0; i < bones.length; i ++ ) {\n\n var bone = bones[ i ];\n\n if ( bone.parent && bone.parent.isBone ) {\n\n vertices.push( 0, 0, 0 );\n vertices.push( 0, 0, 0 );\n colors.push( color1.r, color1.g, color1.b );\n colors.push( color2.r, color2.g, color2.b );\n\n }\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors, depthTest: false, depthWrite: false, transparent: true } );\n\n LineSegments.call( this, geometry, material );\n\n this.root = object;\n this.bones = bones;\n\n this.matrix = object.matrixWorld;\n this.matrixAutoUpdate = false;\n\n }\n\n SkeletonHelper.prototype = Object.create( LineSegments.prototype );\n SkeletonHelper.prototype.constructor = SkeletonHelper;\n\n SkeletonHelper.prototype.updateMatrixWorld = function () {\n\n var vector = new Vector3();\n\n var boneMatrix = new Matrix4();\n var matrixWorldInv = new Matrix4();\n\n return function updateMatrixWorld( force ) {\n\n var bones = this.bones;\n\n var geometry = this.geometry;\n var position = geometry.getAttribute( 'position' );\n\n matrixWorldInv.getInverse( this.root.matrixWorld );\n\n for ( var i = 0, j = 0; i < bones.length; i ++ ) {\n\n var bone = bones[ i ];\n\n if ( bone.parent && bone.parent.isBone ) {\n\n boneMatrix.multiplyMatrices( matrixWorldInv, bone.matrixWorld );\n vector.setFromMatrixPosition( boneMatrix );\n position.setXYZ( j, vector.x, vector.y, vector.z );\n\n boneMatrix.multiplyMatrices( matrixWorldInv, bone.parent.matrixWorld );\n vector.setFromMatrixPosition( boneMatrix );\n position.setXYZ( j + 1, vector.x, vector.y, vector.z );\n\n j += 2;\n\n }\n\n }\n\n geometry.getAttribute( 'position' ).needsUpdate = true;\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n }();\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function PointLightHelper( light, sphereSize, color ) {\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.color = color;\n\n var geometry = new SphereBufferGeometry( sphereSize, 4, 2 );\n var material = new MeshBasicMaterial( { wireframe: true, fog: false } );\n\n Mesh.call( this, geometry, material );\n\n this.matrix = this.light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.update();\n\n\n /*\n var distanceGeometry = new THREE.IcosahedronGeometry( 1, 2 );\n var distanceMaterial = new THREE.MeshBasicMaterial( { color: hexColor, fog: false, wireframe: true, opacity: 0.1, transparent: true } );\n\n this.lightSphere = new THREE.Mesh( bulbGeometry, bulbMaterial );\n this.lightDistance = new THREE.Mesh( distanceGeometry, distanceMaterial );\n\n var d = light.distance;\n\n if ( d === 0.0 ) {\n\n this.lightDistance.visible = false;\n\n } else {\n\n this.lightDistance.scale.set( d, d, d );\n\n }\n\n this.add( this.lightDistance );\n */\n\n }\n\n PointLightHelper.prototype = Object.create( Mesh.prototype );\n PointLightHelper.prototype.constructor = PointLightHelper;\n\n PointLightHelper.prototype.dispose = function () {\n\n this.geometry.dispose();\n this.material.dispose();\n\n };\n\n PointLightHelper.prototype.update = function () {\n\n if ( this.color !== undefined ) {\n\n this.material.color.set( this.color );\n\n } else {\n\n this.material.color.copy( this.light.color );\n\n }\n\n /*\n var d = this.light.distance;\n\n if ( d === 0.0 ) {\n\n this.lightDistance.visible = false;\n\n } else {\n\n this.lightDistance.visible = true;\n this.lightDistance.scale.set( d, d, d );\n\n }\n */\n\n };\n\n /**\n * @author abelnation / http://github.com/abelnation\n * @author Mugen87 / http://github.com/Mugen87\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function RectAreaLightHelper( light, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var material = new LineBasicMaterial( { fog: false } );\n\n var geometry = new BufferGeometry();\n\n geometry.addAttribute( 'position', new BufferAttribute( new Float32Array( 5 * 3 ), 3 ) );\n\n this.line = new Line( geometry, material );\n this.add( this.line );\n\n\n this.update();\n\n }\n\n RectAreaLightHelper.prototype = Object.create( Object3D.prototype );\n RectAreaLightHelper.prototype.constructor = RectAreaLightHelper;\n\n RectAreaLightHelper.prototype.dispose = function () {\n\n this.children[ 0 ].geometry.dispose();\n this.children[ 0 ].material.dispose();\n\n };\n\n RectAreaLightHelper.prototype.update = function () {\n\n // calculate new dimensions of the helper\n\n var hx = this.light.width * 0.5;\n var hy = this.light.height * 0.5;\n\n var position = this.line.geometry.attributes.position;\n var array = position.array;\n\n // update vertices\n\n array[ 0 ] = hx; array[ 1 ] = - hy; array[ 2 ] = 0;\n array[ 3 ] = hx; array[ 4 ] = hy; array[ 5 ] = 0;\n array[ 6 ] = - hx; array[ 7 ] = hy; array[ 8 ] = 0;\n array[ 9 ] = - hx; array[ 10 ] = - hy; array[ 11 ] = 0;\n array[ 12 ] = hx; array[ 13 ] = - hy; array[ 14 ] = 0;\n\n position.needsUpdate = true;\n\n if ( this.color !== undefined ) {\n\n this.line.material.color.set( this.color );\n\n } else {\n\n this.line.material.color.copy( this.light.color );\n\n }\n\n };\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / https://github.com/Mugen87\n */\n\n function HemisphereLightHelper( light, size, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n var geometry = new OctahedronBufferGeometry( size );\n geometry.rotateY( Math.PI * 0.5 );\n\n this.material = new MeshBasicMaterial( { wireframe: true, fog: false } );\n if ( this.color === undefined ) this.material.vertexColors = VertexColors;\n\n var position = geometry.getAttribute( 'position' );\n var colors = new Float32Array( position.count * 3 );\n\n geometry.addAttribute( 'color', new BufferAttribute( colors, 3 ) );\n\n this.add( new Mesh( geometry, this.material ) );\n\n this.update();\n\n }\n\n HemisphereLightHelper.prototype = Object.create( Object3D.prototype );\n HemisphereLightHelper.prototype.constructor = HemisphereLightHelper;\n\n HemisphereLightHelper.prototype.dispose = function () {\n\n this.children[ 0 ].geometry.dispose();\n this.children[ 0 ].material.dispose();\n\n };\n\n HemisphereLightHelper.prototype.update = function () {\n\n var vector = new Vector3();\n\n var color1 = new Color();\n var color2 = new Color();\n\n return function update() {\n\n var mesh = this.children[ 0 ];\n\n if ( this.color !== undefined ) {\n\n this.material.color.set( this.color );\n\n } else {\n\n var colors = mesh.geometry.getAttribute( 'color' );\n\n color1.copy( this.light.color );\n color2.copy( this.light.groundColor );\n\n for ( var i = 0, l = colors.count; i < l; i ++ ) {\n\n var color = ( i < ( l / 2 ) ) ? color1 : color2;\n\n colors.setXYZ( i, color.r, color.g, color.b );\n\n }\n\n colors.needsUpdate = true;\n\n }\n\n mesh.lookAt( vector.setFromMatrixPosition( this.light.matrixWorld ).negate() );\n\n };\n\n }();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function GridHelper( size, divisions, color1, color2 ) {\n\n size = size || 10;\n divisions = divisions || 10;\n color1 = new Color( color1 !== undefined ? color1 : 0x444444 );\n color2 = new Color( color2 !== undefined ? color2 : 0x888888 );\n\n var center = divisions / 2;\n var step = size / divisions;\n var halfSize = size / 2;\n\n var vertices = [], colors = [];\n\n for ( var i = 0, j = 0, k = - halfSize; i <= divisions; i ++, k += step ) {\n\n vertices.push( - halfSize, 0, k, halfSize, 0, k );\n vertices.push( k, 0, - halfSize, k, 0, halfSize );\n\n var color = i === center ? color1 : color2;\n\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n color.toArray( colors, j ); j += 3;\n\n }\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n GridHelper.prototype = Object.create( LineSegments.prototype );\n GridHelper.prototype.constructor = GridHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / http://github.com/Mugen87\n * @author Hectate / http://www.github.com/Hectate\n */\n\n function PolarGridHelper( radius, radials, circles, divisions, color1, color2 ) {\n\n radius = radius || 10;\n radials = radials || 16;\n circles = circles || 8;\n divisions = divisions || 64;\n color1 = new Color( color1 !== undefined ? color1 : 0x444444 );\n color2 = new Color( color2 !== undefined ? color2 : 0x888888 );\n\n var vertices = [];\n var colors = [];\n\n var x, z;\n var v, i, j, r, color;\n\n // create the radials\n\n for ( i = 0; i <= radials; i ++ ) {\n\n v = ( i / radials ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * radius;\n z = Math.cos( v ) * radius;\n\n vertices.push( 0, 0, 0 );\n vertices.push( x, 0, z );\n\n color = ( i & 1 ) ? color1 : color2;\n\n colors.push( color.r, color.g, color.b );\n colors.push( color.r, color.g, color.b );\n\n }\n\n // create the circles\n\n for ( i = 0; i <= circles; i ++ ) {\n\n color = ( i & 1 ) ? color1 : color2;\n\n r = radius - ( radius / circles * i );\n\n for ( j = 0; j < divisions; j ++ ) {\n\n // first vertex\n\n v = ( j / divisions ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * r;\n z = Math.cos( v ) * r;\n\n vertices.push( x, 0, z );\n colors.push( color.r, color.g, color.b );\n\n // second vertex\n\n v = ( ( j + 1 ) / divisions ) * ( Math.PI * 2 );\n\n x = Math.sin( v ) * r;\n z = Math.cos( v ) * r;\n\n vertices.push( x, 0, z );\n colors.push( color.r, color.g, color.b );\n\n }\n\n }\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n PolarGridHelper.prototype = Object.create( LineSegments.prototype );\n PolarGridHelper.prototype.constructor = PolarGridHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function FaceNormalsHelper( object, size, hex, linewidth ) {\n\n // FaceNormalsHelper only supports THREE.Geometry\n\n this.object = object;\n\n this.size = ( size !== undefined ) ? size : 1;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var width = ( linewidth !== undefined ) ? linewidth : 1;\n\n //\n\n var nNormals = 0;\n\n var objGeometry = this.object.geometry;\n\n if ( objGeometry && objGeometry.isGeometry ) {\n\n nNormals = objGeometry.faces.length;\n\n } else {\n\n console.warn( 'THREE.FaceNormalsHelper: only THREE.Geometry is supported. Use THREE.VertexNormalsHelper, instead.' );\n\n }\n\n //\n\n var geometry = new BufferGeometry();\n\n var positions = new Float32BufferAttribute( nNormals * 2 * 3, 3 );\n\n geometry.addAttribute( 'position', positions );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color, linewidth: width } ) );\n\n //\n\n this.matrixAutoUpdate = false;\n this.update();\n\n }\n\n FaceNormalsHelper.prototype = Object.create( LineSegments.prototype );\n FaceNormalsHelper.prototype.constructor = FaceNormalsHelper;\n\n FaceNormalsHelper.prototype.update = ( function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var normalMatrix = new Matrix3();\n\n return function update() {\n\n this.object.updateMatrixWorld( true );\n\n normalMatrix.getNormalMatrix( this.object.matrixWorld );\n\n var matrixWorld = this.object.matrixWorld;\n\n var position = this.geometry.attributes.position;\n\n //\n\n var objGeometry = this.object.geometry;\n\n var vertices = objGeometry.vertices;\n\n var faces = objGeometry.faces;\n\n var idx = 0;\n\n for ( var i = 0, l = faces.length; i < l; i ++ ) {\n\n var face = faces[ i ];\n\n var normal = face.normal;\n\n v1.copy( vertices[ face.a ] )\n .add( vertices[ face.b ] )\n .add( vertices[ face.c ] )\n .divideScalar( 3 )\n .applyMatrix4( matrixWorld );\n\n v2.copy( normal ).applyMatrix3( normalMatrix ).normalize().multiplyScalar( this.size ).add( v1 );\n\n position.setXYZ( idx, v1.x, v1.y, v1.z );\n\n idx = idx + 1;\n\n position.setXYZ( idx, v2.x, v2.y, v2.z );\n\n idx = idx + 1;\n\n }\n\n position.needsUpdate = true;\n\n };\n\n }() );\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author mrdoob / http://mrdoob.com/\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function DirectionalLightHelper( light, size, color ) {\n\n Object3D.call( this );\n\n this.light = light;\n this.light.updateMatrixWorld();\n\n this.matrix = light.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.color = color;\n\n if ( size === undefined ) size = 1;\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( [\n - size, size, 0,\n size, size, 0,\n size, - size, 0,\n - size, - size, 0,\n - size, size, 0\n ], 3 ) );\n\n var material = new LineBasicMaterial( { fog: false } );\n\n this.lightPlane = new Line( geometry, material );\n this.add( this.lightPlane );\n\n geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 0, 1 ], 3 ) );\n\n this.targetLine = new Line( geometry, material );\n this.add( this.targetLine );\n\n this.update();\n\n }\n\n DirectionalLightHelper.prototype = Object.create( Object3D.prototype );\n DirectionalLightHelper.prototype.constructor = DirectionalLightHelper;\n\n DirectionalLightHelper.prototype.dispose = function () {\n\n this.lightPlane.geometry.dispose();\n this.lightPlane.material.dispose();\n this.targetLine.geometry.dispose();\n this.targetLine.material.dispose();\n\n };\n\n DirectionalLightHelper.prototype.update = function () {\n\n var v1 = new Vector3();\n var v2 = new Vector3();\n var v3 = new Vector3();\n\n return function update() {\n\n v1.setFromMatrixPosition( this.light.matrixWorld );\n v2.setFromMatrixPosition( this.light.target.matrixWorld );\n v3.subVectors( v2, v1 );\n\n this.lightPlane.lookAt( v3 );\n\n if ( this.color !== undefined ) {\n\n this.lightPlane.material.color.set( this.color );\n this.targetLine.material.color.set( this.color );\n\n } else {\n\n this.lightPlane.material.color.copy( this.light.color );\n this.targetLine.material.color.copy( this.light.color );\n\n }\n\n this.targetLine.lookAt( v3 );\n this.targetLine.scale.z = v3.length();\n\n };\n\n }();\n\n /**\n * @author alteredq / http://alteredqualia.com/\n * @author Mugen87 / https://github.com/Mugen87\n *\n * - shows frustum, line of sight and up of the camera\n * - suitable for fast updates\n * - based on frustum visualization in lightgl.js shadowmap example\n * http://evanw.github.com/lightgl.js/tests/shadowmap.html\n */\n\n function CameraHelper( camera ) {\n\n var geometry = new BufferGeometry();\n var material = new LineBasicMaterial( { color: 0xffffff, vertexColors: FaceColors } );\n\n var vertices = [];\n var colors = [];\n\n var pointMap = {};\n\n // colors\n\n var colorFrustum = new Color( 0xffaa00 );\n var colorCone = new Color( 0xff0000 );\n var colorUp = new Color( 0x00aaff );\n var colorTarget = new Color( 0xffffff );\n var colorCross = new Color( 0x333333 );\n\n // near\n\n addLine( 'n1', 'n2', colorFrustum );\n addLine( 'n2', 'n4', colorFrustum );\n addLine( 'n4', 'n3', colorFrustum );\n addLine( 'n3', 'n1', colorFrustum );\n\n // far\n\n addLine( 'f1', 'f2', colorFrustum );\n addLine( 'f2', 'f4', colorFrustum );\n addLine( 'f4', 'f3', colorFrustum );\n addLine( 'f3', 'f1', colorFrustum );\n\n // sides\n\n addLine( 'n1', 'f1', colorFrustum );\n addLine( 'n2', 'f2', colorFrustum );\n addLine( 'n3', 'f3', colorFrustum );\n addLine( 'n4', 'f4', colorFrustum );\n\n // cone\n\n addLine( 'p', 'n1', colorCone );\n addLine( 'p', 'n2', colorCone );\n addLine( 'p', 'n3', colorCone );\n addLine( 'p', 'n4', colorCone );\n\n // up\n\n addLine( 'u1', 'u2', colorUp );\n addLine( 'u2', 'u3', colorUp );\n addLine( 'u3', 'u1', colorUp );\n\n // target\n\n addLine( 'c', 't', colorTarget );\n addLine( 'p', 'c', colorCross );\n\n // cross\n\n addLine( 'cn1', 'cn2', colorCross );\n addLine( 'cn3', 'cn4', colorCross );\n\n addLine( 'cf1', 'cf2', colorCross );\n addLine( 'cf3', 'cf4', colorCross );\n\n function addLine( a, b, color ) {\n\n addPoint( a, color );\n addPoint( b, color );\n\n }\n\n function addPoint( id, color ) {\n\n vertices.push( 0, 0, 0 );\n colors.push( color.r, color.g, color.b );\n\n if ( pointMap[ id ] === undefined ) {\n\n pointMap[ id ] = [];\n\n }\n\n pointMap[ id ].push( ( vertices.length / 3 ) - 1 );\n\n }\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n LineSegments.call( this, geometry, material );\n\n this.camera = camera;\n if ( this.camera.updateProjectionMatrix ) this.camera.updateProjectionMatrix();\n\n this.matrix = camera.matrixWorld;\n this.matrixAutoUpdate = false;\n\n this.pointMap = pointMap;\n\n this.update();\n\n }\n\n CameraHelper.prototype = Object.create( LineSegments.prototype );\n CameraHelper.prototype.constructor = CameraHelper;\n\n CameraHelper.prototype.update = function () {\n\n var geometry, pointMap;\n\n var vector = new Vector3();\n var camera = new Camera();\n\n function setPoint( point, x, y, z ) {\n\n vector.set( x, y, z ).unproject( camera );\n\n var points = pointMap[ point ];\n\n if ( points !== undefined ) {\n\n var position = geometry.getAttribute( 'position' );\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n position.setXYZ( points[ i ], vector.x, vector.y, vector.z );\n\n }\n\n }\n\n }\n\n return function update() {\n\n geometry = this.geometry;\n pointMap = this.pointMap;\n\n var w = 1, h = 1;\n\n // we need just camera projection matrix\n // world matrix must be identity\n\n camera.projectionMatrix.copy( this.camera.projectionMatrix );\n\n // center / target\n\n setPoint( 'c', 0, 0, - 1 );\n setPoint( 't', 0, 0, 1 );\n\n // near\n\n setPoint( 'n1', - w, - h, - 1 );\n setPoint( 'n2', w, - h, - 1 );\n setPoint( 'n3', - w, h, - 1 );\n setPoint( 'n4', w, h, - 1 );\n\n // far\n\n setPoint( 'f1', - w, - h, 1 );\n setPoint( 'f2', w, - h, 1 );\n setPoint( 'f3', - w, h, 1 );\n setPoint( 'f4', w, h, 1 );\n\n // up\n\n setPoint( 'u1', w * 0.7, h * 1.1, - 1 );\n setPoint( 'u2', - w * 0.7, h * 1.1, - 1 );\n setPoint( 'u3', 0, h * 2, - 1 );\n\n // cross\n\n setPoint( 'cf1', - w, 0, 1 );\n setPoint( 'cf2', w, 0, 1 );\n setPoint( 'cf3', 0, - h, 1 );\n setPoint( 'cf4', 0, h, 1 );\n\n setPoint( 'cn1', - w, 0, - 1 );\n setPoint( 'cn2', w, 0, - 1 );\n setPoint( 'cn3', 0, - h, - 1 );\n setPoint( 'cn4', 0, h, - 1 );\n\n geometry.getAttribute( 'position' ).needsUpdate = true;\n\n };\n\n }();\n\n /**\n * @author mrdoob / http://mrdoob.com/\n * @author Mugen87 / http://github.com/Mugen87\n */\n\n function BoxHelper( object, color ) {\n\n this.object = object;\n\n if ( color === undefined ) color = 0xffff00;\n\n var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n var positions = new Float32Array( 8 * 3 );\n\n var geometry = new BufferGeometry();\n geometry.setIndex( new BufferAttribute( indices, 1 ) );\n geometry.addAttribute( 'position', new BufferAttribute( positions, 3 ) );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n this.matrixAutoUpdate = false;\n\n this.update();\n\n }\n\n BoxHelper.prototype = Object.create( LineSegments.prototype );\n BoxHelper.prototype.constructor = BoxHelper;\n\n BoxHelper.prototype.update = ( function () {\n\n var box = new Box3();\n\n return function update( object ) {\n\n if ( object !== undefined ) {\n\n console.warn( 'THREE.BoxHelper: .update() has no longer arguments.' );\n\n }\n\n if ( this.object !== undefined ) {\n\n box.setFromObject( this.object );\n\n }\n\n if ( box.isEmpty() ) return;\n\n var min = box.min;\n var max = box.max;\n\n /*\n 5____4\n 1/___0/|\n | 6__|_7\n 2/___3/\n\n 0: max.x, max.y, max.z\n 1: min.x, max.y, max.z\n 2: min.x, min.y, max.z\n 3: max.x, min.y, max.z\n 4: max.x, max.y, min.z\n 5: min.x, max.y, min.z\n 6: min.x, min.y, min.z\n 7: max.x, min.y, min.z\n */\n\n var position = this.geometry.attributes.position;\n var array = position.array;\n\n array[ 0 ] = max.x; array[ 1 ] = max.y; array[ 2 ] = max.z;\n array[ 3 ] = min.x; array[ 4 ] = max.y; array[ 5 ] = max.z;\n array[ 6 ] = min.x; array[ 7 ] = min.y; array[ 8 ] = max.z;\n array[ 9 ] = max.x; array[ 10 ] = min.y; array[ 11 ] = max.z;\n array[ 12 ] = max.x; array[ 13 ] = max.y; array[ 14 ] = min.z;\n array[ 15 ] = min.x; array[ 16 ] = max.y; array[ 17 ] = min.z;\n array[ 18 ] = min.x; array[ 19 ] = min.y; array[ 20 ] = min.z;\n array[ 21 ] = max.x; array[ 22 ] = min.y; array[ 23 ] = min.z;\n\n position.needsUpdate = true;\n\n this.geometry.computeBoundingSphere();\n\n };\n\n } )();\n\n BoxHelper.prototype.setFromObject = function ( object ) {\n\n this.object = object;\n this.update();\n\n return this;\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function Box3Helper( box, hex ) {\n\n this.type = 'Box3Helper';\n\n this.box = box;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var indices = new Uint16Array( [ 0, 1, 1, 2, 2, 3, 3, 0, 4, 5, 5, 6, 6, 7, 7, 4, 0, 4, 1, 5, 2, 6, 3, 7 ] );\n\n var positions = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, - 1, - 1, 1, - 1, - 1, - 1, - 1, 1, - 1, - 1 ];\n\n var geometry = new BufferGeometry();\n\n geometry.setIndex( new BufferAttribute( indices, 1 ) );\n\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n\n LineSegments.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n this.geometry.computeBoundingSphere();\n\n }\n\n Box3Helper.prototype = Object.create( LineSegments.prototype );\n Box3Helper.prototype.constructor = Box3Helper;\n\n Box3Helper.prototype.updateMatrixWorld = function ( force ) {\n\n var box = this.box;\n\n if ( box.isEmpty() ) return;\n\n box.getCenter( this.position );\n\n box.getSize( this.scale );\n\n this.scale.multiplyScalar( 0.5 );\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n */\n\n function PlaneHelper( plane, size, hex ) {\n\n this.type = 'PlaneHelper';\n\n this.plane = plane;\n\n this.size = ( size === undefined ) ? 1 : size;\n\n var color = ( hex !== undefined ) ? hex : 0xffff00;\n\n var positions = [ 1, - 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, - 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 ];\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( positions, 3 ) );\n geometry.computeBoundingSphere();\n\n Line.call( this, geometry, new LineBasicMaterial( { color: color } ) );\n\n //\n\n var positions2 = [ 1, 1, 1, - 1, 1, 1, - 1, - 1, 1, 1, 1, 1, - 1, - 1, 1, 1, - 1, 1 ];\n\n var geometry2 = new BufferGeometry();\n geometry2.addAttribute( 'position', new Float32BufferAttribute( positions2, 3 ) );\n geometry2.computeBoundingSphere();\n\n this.add( new Mesh( geometry2, new MeshBasicMaterial( { color: color, opacity: 0.2, transparent: true, depthWrite: false } ) ) );\n\n }\n\n PlaneHelper.prototype = Object.create( Line.prototype );\n PlaneHelper.prototype.constructor = PlaneHelper;\n\n PlaneHelper.prototype.updateMatrixWorld = function ( force ) {\n\n var scale = - this.plane.constant;\n\n if ( Math.abs( scale ) < 1e-8 ) scale = 1e-8; // sign does not matter\n\n this.scale.set( 0.5 * this.size, 0.5 * this.size, scale );\n\n this.lookAt( this.plane.normal );\n\n Object3D.prototype.updateMatrixWorld.call( this, force );\n\n };\n\n /**\n * @author WestLangley / http://github.com/WestLangley\n * @author zz85 / http://github.com/zz85\n * @author bhouston / http://clara.io\n *\n * Creates an arrow for visualizing directions\n *\n * Parameters:\n * dir - Vector3\n * origin - Vector3\n * length - Number\n * color - color in hex value\n * headLength - Number\n * headWidth - Number\n */\n\n var lineGeometry;\n var coneGeometry;\n\n function ArrowHelper( dir, origin, length, color, headLength, headWidth ) {\n\n // dir is assumed to be normalized\n\n Object3D.call( this );\n\n if ( color === undefined ) color = 0xffff00;\n if ( length === undefined ) length = 1;\n if ( headLength === undefined ) headLength = 0.2 * length;\n if ( headWidth === undefined ) headWidth = 0.2 * headLength;\n\n if ( lineGeometry === undefined ) {\n\n lineGeometry = new BufferGeometry();\n lineGeometry.addAttribute( 'position', new Float32BufferAttribute( [ 0, 0, 0, 0, 1, 0 ], 3 ) );\n\n coneGeometry = new CylinderBufferGeometry( 0, 0.5, 1, 5, 1 );\n coneGeometry.translate( 0, - 0.5, 0 );\n\n }\n\n this.position.copy( origin );\n\n this.line = new Line( lineGeometry, new LineBasicMaterial( { color: color } ) );\n this.line.matrixAutoUpdate = false;\n this.add( this.line );\n\n this.cone = new Mesh( coneGeometry, new MeshBasicMaterial( { color: color } ) );\n this.cone.matrixAutoUpdate = false;\n this.add( this.cone );\n\n this.setDirection( dir );\n this.setLength( length, headLength, headWidth );\n\n }\n\n ArrowHelper.prototype = Object.create( Object3D.prototype );\n ArrowHelper.prototype.constructor = ArrowHelper;\n\n ArrowHelper.prototype.setDirection = ( function () {\n\n var axis = new Vector3();\n var radians;\n\n return function setDirection( dir ) {\n\n // dir is assumed to be normalized\n\n if ( dir.y > 0.99999 ) {\n\n this.quaternion.set( 0, 0, 0, 1 );\n\n } else if ( dir.y < - 0.99999 ) {\n\n this.quaternion.set( 1, 0, 0, 0 );\n\n } else {\n\n axis.set( dir.z, 0, - dir.x ).normalize();\n\n radians = Math.acos( dir.y );\n\n this.quaternion.setFromAxisAngle( axis, radians );\n\n }\n\n };\n\n }() );\n\n ArrowHelper.prototype.setLength = function ( length, headLength, headWidth ) {\n\n if ( headLength === undefined ) headLength = 0.2 * length;\n if ( headWidth === undefined ) headWidth = 0.2 * headLength;\n\n this.line.scale.set( 1, Math.max( 0, length - headLength ), 1 );\n this.line.updateMatrix();\n\n this.cone.scale.set( headWidth, headLength, headWidth );\n this.cone.position.y = length;\n this.cone.updateMatrix();\n\n };\n\n ArrowHelper.prototype.setColor = function ( color ) {\n\n this.line.material.color.copy( color );\n this.cone.material.color.copy( color );\n\n };\n\n /**\n * @author sroucheray / http://sroucheray.org/\n * @author mrdoob / http://mrdoob.com/\n */\n\n function AxesHelper( size ) {\n\n size = size || 1;\n\n var vertices = [\n 0, 0, 0, size, 0, 0,\n 0, 0, 0, 0, size, 0,\n 0, 0, 0, 0, 0, size\n ];\n\n var colors = [\n 1, 0, 0, 1, 0.6, 0,\n 0, 1, 0, 0.6, 1, 0,\n 0, 0, 1, 0, 0.6, 1\n ];\n\n var geometry = new BufferGeometry();\n geometry.addAttribute( 'position', new Float32BufferAttribute( vertices, 3 ) );\n geometry.addAttribute( 'color', new Float32BufferAttribute( colors, 3 ) );\n\n var material = new LineBasicMaterial( { vertexColors: VertexColors } );\n\n LineSegments.call( this, geometry, material );\n\n }\n\n AxesHelper.prototype = Object.create( LineSegments.prototype );\n AxesHelper.prototype.constructor = AxesHelper;\n\n /**\n * @author mrdoob / http://mrdoob.com/\n */\n\n function Face4( a, b, c, d, normal, color, materialIndex ) {\n\n console.warn( 'THREE.Face4 has been removed. A THREE.Face3 will be created instead.' );\n return new Face3( a, b, c, normal, color, materialIndex );\n\n }\n\n var LineStrip = 0;\n\n var LinePieces = 1;\n\n function MeshFaceMaterial( materials ) {\n\n console.warn( 'THREE.MeshFaceMaterial has been removed. Use an Array instead.' );\n return materials;\n\n }\n\n function MultiMaterial( materials ) {\n\n if ( materials === undefined ) materials = [];\n\n console.warn( 'THREE.MultiMaterial has been removed. Use an Array instead.' );\n materials.isMultiMaterial = true;\n materials.materials = materials;\n materials.clone = function () {\n\n return materials.slice();\n\n };\n return materials;\n\n }\n\n function PointCloud( geometry, material ) {\n\n console.warn( 'THREE.PointCloud has been renamed to THREE.Points.' );\n return new Points( geometry, material );\n\n }\n\n function Particle( material ) {\n\n console.warn( 'THREE.Particle has been renamed to THREE.Sprite.' );\n return new Sprite( material );\n\n }\n\n function ParticleSystem( geometry, material ) {\n\n console.warn( 'THREE.ParticleSystem has been renamed to THREE.Points.' );\n return new Points( geometry, material );\n\n }\n\n function PointCloudMaterial( parameters ) {\n\n console.warn( 'THREE.PointCloudMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function ParticleBasicMaterial( parameters ) {\n\n console.warn( 'THREE.ParticleBasicMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function ParticleSystemMaterial( parameters ) {\n\n console.warn( 'THREE.ParticleSystemMaterial has been renamed to THREE.PointsMaterial.' );\n return new PointsMaterial( parameters );\n\n }\n\n function Vertex( x, y, z ) {\n\n console.warn( 'THREE.Vertex has been removed. Use THREE.Vector3 instead.' );\n return new Vector3( x, y, z );\n\n }\n\n //\n\n function DynamicBufferAttribute( array, itemSize ) {\n\n console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' );\n return new BufferAttribute( array, itemSize ).setDynamic( true );\n\n }\n\n function Int8Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int8Attribute has been removed. Use new THREE.Int8BufferAttribute() instead.' );\n return new Int8BufferAttribute( array, itemSize );\n\n }\n\n function Uint8Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint8Attribute has been removed. Use new THREE.Uint8BufferAttribute() instead.' );\n return new Uint8BufferAttribute( array, itemSize );\n\n }\n\n function Uint8ClampedAttribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint8ClampedAttribute has been removed. Use new THREE.Uint8ClampedBufferAttribute() instead.' );\n return new Uint8ClampedBufferAttribute( array, itemSize );\n\n }\n\n function Int16Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int16Attribute has been removed. Use new THREE.Int16BufferAttribute() instead.' );\n return new Int16BufferAttribute( array, itemSize );\n\n }\n\n function Uint16Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint16Attribute has been removed. Use new THREE.Uint16BufferAttribute() instead.' );\n return new Uint16BufferAttribute( array, itemSize );\n\n }\n\n function Int32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Int32Attribute has been removed. Use new THREE.Int32BufferAttribute() instead.' );\n return new Int32BufferAttribute( array, itemSize );\n\n }\n\n function Uint32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Uint32Attribute has been removed. Use new THREE.Uint32BufferAttribute() instead.' );\n return new Uint32BufferAttribute( array, itemSize );\n\n }\n\n function Float32Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Float32Attribute has been removed. Use new THREE.Float32BufferAttribute() instead.' );\n return new Float32BufferAttribute( array, itemSize );\n\n }\n\n function Float64Attribute( array, itemSize ) {\n\n console.warn( 'THREE.Float64Attribute has been removed. Use new THREE.Float64BufferAttribute() instead.' );\n return new Float64BufferAttribute( array, itemSize );\n\n }\n\n //\n\n Curve.create = function ( construct, getPoint ) {\n\n console.log( 'THREE.Curve.create() has been deprecated' );\n\n construct.prototype = Object.create( Curve.prototype );\n construct.prototype.constructor = construct;\n construct.prototype.getPoint = getPoint;\n\n return construct;\n\n };\n\n //\n\n Object.assign( CurvePath.prototype, {\n\n createPointsGeometry: function ( divisions ) {\n\n console.warn( 'THREE.CurvePath: .createPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n // generate geometry from path points (for Line or Points objects)\n\n var pts = this.getPoints( divisions );\n return this.createGeometry( pts );\n\n },\n\n createSpacedPointsGeometry: function ( divisions ) {\n\n console.warn( 'THREE.CurvePath: .createSpacedPointsGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n // generate geometry from equidistant sampling along the path\n\n var pts = this.getSpacedPoints( divisions );\n return this.createGeometry( pts );\n\n },\n\n createGeometry: function ( points ) {\n\n console.warn( 'THREE.CurvePath: .createGeometry() has been removed. Use new THREE.Geometry().setFromPoints( points ) instead.' );\n\n var geometry = new Geometry();\n\n for ( var i = 0, l = points.length; i < l; i ++ ) {\n\n var point = points[ i ];\n geometry.vertices.push( new Vector3( point.x, point.y, point.z || 0 ) );\n\n }\n\n return geometry;\n\n }\n\n } );\n\n //\n\n Object.assign( Path.prototype, {\n\n fromPoints: function ( points ) {\n\n console.warn( 'THREE.Path: .fromPoints() has been renamed to .setFromPoints().' );\n this.setFromPoints( points );\n\n }\n\n } );\n\n //\n\n function ClosedSplineCurve3( points ) {\n\n console.warn( 'THREE.ClosedSplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n this.closed = true;\n\n }\n\n ClosedSplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );\n\n //\n\n function SplineCurve3( points ) {\n\n console.warn( 'THREE.SplineCurve3 has been deprecated. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n\n }\n\n SplineCurve3.prototype = Object.create( CatmullRomCurve3.prototype );\n\n //\n\n function Spline( points ) {\n\n console.warn( 'THREE.Spline has been removed. Use THREE.CatmullRomCurve3 instead.' );\n\n CatmullRomCurve3.call( this, points );\n this.type = 'catmullrom';\n\n }\n\n Spline.prototype = Object.create( CatmullRomCurve3.prototype );\n\n Object.assign( Spline.prototype, {\n\n initFromArray: function ( /* a */ ) {\n\n console.error( 'THREE.Spline: .initFromArray() has been removed.' );\n\n },\n getControlPointsArray: function ( /* optionalTarget */ ) {\n\n console.error( 'THREE.Spline: .getControlPointsArray() has been removed.' );\n\n },\n reparametrizeByArcLength: function ( /* samplingCoef */ ) {\n\n console.error( 'THREE.Spline: .reparametrizeByArcLength() has been removed.' );\n\n }\n\n } );\n\n //\n\n function AxisHelper( size ) {\n\n console.warn( 'THREE.AxisHelper has been renamed to THREE.AxesHelper.' );\n return new AxesHelper( size );\n\n }\n\n function BoundingBoxHelper( object, color ) {\n\n console.warn( 'THREE.BoundingBoxHelper has been deprecated. Creating a THREE.BoxHelper instead.' );\n return new BoxHelper( object, color );\n\n }\n\n function EdgesHelper( object, hex ) {\n\n console.warn( 'THREE.EdgesHelper has been removed. Use THREE.EdgesGeometry instead.' );\n return new LineSegments( new EdgesGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );\n\n }\n\n GridHelper.prototype.setColors = function () {\n\n console.error( 'THREE.GridHelper: setColors() has been deprecated, pass them in the constructor instead.' );\n\n };\n\n SkeletonHelper.prototype.update = function () {\n\n console.error( 'THREE.SkeletonHelper: update() no longer needs to be called.' );\n\n };\n\n function WireframeHelper( object, hex ) {\n\n console.warn( 'THREE.WireframeHelper has been removed. Use THREE.WireframeGeometry instead.' );\n return new LineSegments( new WireframeGeometry( object.geometry ), new LineBasicMaterial( { color: hex !== undefined ? hex : 0xffffff } ) );\n\n }\n\n //\n\n Object.assign( Loader.prototype, {\n\n extractUrlBase: function ( url ) {\n\n console.warn( 'THREE.Loader: .extractUrlBase() has been deprecated. Use THREE.LoaderUtils.extractUrlBase() instead.' );\n return LoaderUtils.extractUrlBase( url );\n\n }\n\n } );\n\n function XHRLoader( manager ) {\n\n console.warn( 'THREE.XHRLoader has been renamed to THREE.FileLoader.' );\n return new FileLoader( manager );\n\n }\n\n function BinaryTextureLoader( manager ) {\n\n console.warn( 'THREE.BinaryTextureLoader has been renamed to THREE.DataTextureLoader.' );\n return new DataTextureLoader( manager );\n\n }\n\n //\n\n Object.assign( Box2.prototype, {\n\n center: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box2: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n },\n empty: function () {\n\n console.warn( 'THREE.Box2: .empty() has been renamed to .isEmpty().' );\n return this.isEmpty();\n\n },\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Box2: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n size: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box2: .size() has been renamed to .getSize().' );\n return this.getSize( optionalTarget );\n\n }\n } );\n\n Object.assign( Box3.prototype, {\n\n center: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box3: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n },\n empty: function () {\n\n console.warn( 'THREE.Box3: .empty() has been renamed to .isEmpty().' );\n return this.isEmpty();\n\n },\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Box3: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n isIntersectionSphere: function ( sphere ) {\n\n console.warn( 'THREE.Box3: .isIntersectionSphere() has been renamed to .intersectsSphere().' );\n return this.intersectsSphere( sphere );\n\n },\n size: function ( optionalTarget ) {\n\n console.warn( 'THREE.Box3: .size() has been renamed to .getSize().' );\n return this.getSize( optionalTarget );\n\n }\n } );\n\n Line3.prototype.center = function ( optionalTarget ) {\n\n console.warn( 'THREE.Line3: .center() has been renamed to .getCenter().' );\n return this.getCenter( optionalTarget );\n\n };\n\n Object.assign( _Math, {\n\n random16: function () {\n\n console.warn( 'THREE.Math: .random16() has been deprecated. Use Math.random() instead.' );\n return Math.random();\n\n },\n\n nearestPowerOfTwo: function ( value ) {\n\n console.warn( 'THREE.Math: .nearestPowerOfTwo() has been renamed to .floorPowerOfTwo().' );\n return _Math.floorPowerOfTwo( value );\n\n },\n\n nextPowerOfTwo: function ( value ) {\n\n console.warn( 'THREE.Math: .nextPowerOfTwo() has been renamed to .ceilPowerOfTwo().' );\n return _Math.ceilPowerOfTwo( value );\n\n }\n\n } );\n\n Object.assign( Matrix3.prototype, {\n\n flattenToArrayOffset: function ( array, offset ) {\n\n console.warn( \"THREE.Matrix3: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.\" );\n return this.toArray( array, offset );\n\n },\n multiplyVector3: function ( vector ) {\n\n console.warn( 'THREE.Matrix3: .multiplyVector3() has been removed. Use vector.applyMatrix3( matrix ) instead.' );\n return vector.applyMatrix3( this );\n\n },\n multiplyVector3Array: function ( /* a */ ) {\n\n console.error( 'THREE.Matrix3: .multiplyVector3Array() has been removed.' );\n\n },\n applyToBuffer: function ( buffer /*, offset, length */ ) {\n\n console.warn( 'THREE.Matrix3: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );\n return this.applyToBufferAttribute( buffer );\n\n },\n applyToVector3Array: function ( /* array, offset, length */ ) {\n\n console.error( 'THREE.Matrix3: .applyToVector3Array() has been removed.' );\n\n }\n\n } );\n\n Object.assign( Matrix4.prototype, {\n\n extractPosition: function ( m ) {\n\n console.warn( 'THREE.Matrix4: .extractPosition() has been renamed to .copyPosition().' );\n return this.copyPosition( m );\n\n },\n flattenToArrayOffset: function ( array, offset ) {\n\n console.warn( \"THREE.Matrix4: .flattenToArrayOffset() has been deprecated. Use .toArray() instead.\" );\n return this.toArray( array, offset );\n\n },\n getPosition: function () {\n\n var v1;\n\n return function getPosition() {\n\n if ( v1 === undefined ) v1 = new Vector3();\n console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' );\n return v1.setFromMatrixColumn( this, 3 );\n\n };\n\n }(),\n setRotationFromQuaternion: function ( q ) {\n\n console.warn( 'THREE.Matrix4: .setRotationFromQuaternion() has been renamed to .makeRotationFromQuaternion().' );\n return this.makeRotationFromQuaternion( q );\n\n },\n multiplyToArray: function () {\n\n console.warn( 'THREE.Matrix4: .multiplyToArray() has been removed.' );\n\n },\n multiplyVector3: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .multiplyVector3() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n multiplyVector4: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .multiplyVector4() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n multiplyVector3Array: function ( /* a */ ) {\n\n console.error( 'THREE.Matrix4: .multiplyVector3Array() has been removed.' );\n\n },\n rotateAxis: function ( v ) {\n\n console.warn( 'THREE.Matrix4: .rotateAxis() has been removed. Use Vector3.transformDirection( matrix ) instead.' );\n v.transformDirection( this );\n\n },\n crossVector: function ( vector ) {\n\n console.warn( 'THREE.Matrix4: .crossVector() has been removed. Use vector.applyMatrix4( matrix ) instead.' );\n return vector.applyMatrix4( this );\n\n },\n translate: function () {\n\n console.error( 'THREE.Matrix4: .translate() has been removed.' );\n\n },\n rotateX: function () {\n\n console.error( 'THREE.Matrix4: .rotateX() has been removed.' );\n\n },\n rotateY: function () {\n\n console.error( 'THREE.Matrix4: .rotateY() has been removed.' );\n\n },\n rotateZ: function () {\n\n console.error( 'THREE.Matrix4: .rotateZ() has been removed.' );\n\n },\n rotateByAxis: function () {\n\n console.error( 'THREE.Matrix4: .rotateByAxis() has been removed.' );\n\n },\n applyToBuffer: function ( buffer /*, offset, length */ ) {\n\n console.warn( 'THREE.Matrix4: .applyToBuffer() has been removed. Use matrix.applyToBufferAttribute( attribute ) instead.' );\n return this.applyToBufferAttribute( buffer );\n\n },\n applyToVector3Array: function ( /* array, offset, length */ ) {\n\n console.error( 'THREE.Matrix4: .applyToVector3Array() has been removed.' );\n\n },\n makeFrustum: function ( left, right, bottom, top, near, far ) {\n\n console.warn( 'THREE.Matrix4: .makeFrustum() has been removed. Use .makePerspective( left, right, top, bottom, near, far ) instead.' );\n return this.makePerspective( left, right, top, bottom, near, far );\n\n }\n\n } );\n\n Plane.prototype.isIntersectionLine = function ( line ) {\n\n console.warn( 'THREE.Plane: .isIntersectionLine() has been renamed to .intersectsLine().' );\n return this.intersectsLine( line );\n\n };\n\n Quaternion.prototype.multiplyVector3 = function ( vector ) {\n\n console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );\n return vector.applyQuaternion( this );\n\n };\n\n Object.assign( Ray.prototype, {\n\n isIntersectionBox: function ( box ) {\n\n console.warn( 'THREE.Ray: .isIntersectionBox() has been renamed to .intersectsBox().' );\n return this.intersectsBox( box );\n\n },\n isIntersectionPlane: function ( plane ) {\n\n console.warn( 'THREE.Ray: .isIntersectionPlane() has been renamed to .intersectsPlane().' );\n return this.intersectsPlane( plane );\n\n },\n isIntersectionSphere: function ( sphere ) {\n\n console.warn( 'THREE.Ray: .isIntersectionSphere() has been renamed to .intersectsSphere().' );\n return this.intersectsSphere( sphere );\n\n }\n\n } );\n\n Object.assign( Triangle.prototype, {\n\n area: function () {\n\n console.warn( 'THREE.Triangle: .area() has been renamed to .getArea().' );\n return this.getArea();\n\n },\n barycoordFromPoint: function ( point, target ) {\n\n console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );\n return this.getBarycoord( point, target );\n\n },\n midpoint: function ( target ) {\n\n console.warn( 'THREE.Triangle: .midpoint() has been renamed to .getMidpoint().' );\n return this.getMidpoint( target );\n\n },\n normal: function ( target ) {\n\n console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );\n return this.getNormal( target );\n\n },\n plane: function ( target ) {\n\n console.warn( 'THREE.Triangle: .plane() has been renamed to .getPlane().' );\n return this.getPlane( target );\n\n }\n\n } );\n\n Object.assign( Triangle, {\n\n barycoordFromPoint: function ( point, a, b, c, target ) {\n\n console.warn( 'THREE.Triangle: .barycoordFromPoint() has been renamed to .getBarycoord().' );\n return Triangle.getBarycoord( point, a, b, c, target );\n\n },\n normal: function ( a, b, c, target ) {\n\n console.warn( 'THREE.Triangle: .normal() has been renamed to .getNormal().' );\n return Triangle.getNormal( a, b, c, target );\n\n }\n\n } );\n\n Object.assign( Shape.prototype, {\n\n extractAllPoints: function ( divisions ) {\n\n console.warn( 'THREE.Shape: .extractAllPoints() has been removed. Use .extractPoints() instead.' );\n return this.extractPoints( divisions );\n\n },\n extrude: function ( options ) {\n\n console.warn( 'THREE.Shape: .extrude() has been removed. Use ExtrudeGeometry() instead.' );\n return new ExtrudeGeometry( this, options );\n\n },\n makeGeometry: function ( options ) {\n\n console.warn( 'THREE.Shape: .makeGeometry() has been removed. Use ShapeGeometry() instead.' );\n return new ShapeGeometry( this, options );\n\n }\n\n } );\n\n Object.assign( Vector2.prototype, {\n\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector2: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n distanceToManhattan: function ( v ) {\n\n console.warn( 'THREE.Vector2: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );\n return this.manhattanDistanceTo( v );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector2: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n Object.assign( Vector3.prototype, {\n\n setEulerFromRotationMatrix: function () {\n\n console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );\n\n },\n setEulerFromQuaternion: function () {\n\n console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );\n\n },\n getPositionFromMatrix: function ( m ) {\n\n console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );\n return this.setFromMatrixPosition( m );\n\n },\n getScaleFromMatrix: function ( m ) {\n\n console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );\n return this.setFromMatrixScale( m );\n\n },\n getColumnFromMatrix: function ( index, matrix ) {\n\n console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );\n return this.setFromMatrixColumn( matrix, index );\n\n },\n applyProjection: function ( m ) {\n\n console.warn( 'THREE.Vector3: .applyProjection() has been removed. Use .applyMatrix4( m ) instead.' );\n return this.applyMatrix4( m );\n\n },\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector3: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n distanceToManhattan: function ( v ) {\n\n console.warn( 'THREE.Vector3: .distanceToManhattan() has been renamed to .manhattanDistanceTo().' );\n return this.manhattanDistanceTo( v );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector3: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n Object.assign( Vector4.prototype, {\n\n fromAttribute: function ( attribute, index, offset ) {\n\n console.warn( 'THREE.Vector4: .fromAttribute() has been renamed to .fromBufferAttribute().' );\n return this.fromBufferAttribute( attribute, index, offset );\n\n },\n lengthManhattan: function () {\n\n console.warn( 'THREE.Vector4: .lengthManhattan() has been renamed to .manhattanLength().' );\n return this.manhattanLength();\n\n }\n\n } );\n\n //\n\n Object.assign( Geometry.prototype, {\n\n computeTangents: function () {\n\n console.error( 'THREE.Geometry: .computeTangents() has been removed.' );\n\n },\n computeLineDistances: function () {\n\n console.error( 'THREE.Geometry: .computeLineDistances() has been removed. Use THREE.Line.computeLineDistances() instead.' );\n\n }\n\n } );\n\n Object.assign( Object3D.prototype, {\n\n getChildByName: function ( name ) {\n\n console.warn( 'THREE.Object3D: .getChildByName() has been renamed to .getObjectByName().' );\n return this.getObjectByName( name );\n\n },\n renderDepth: function () {\n\n console.warn( 'THREE.Object3D: .renderDepth has been removed. Use .renderOrder, instead.' );\n\n },\n translate: function ( distance, axis ) {\n\n console.warn( 'THREE.Object3D: .translate() has been removed. Use .translateOnAxis( axis, distance ) instead.' );\n return this.translateOnAxis( axis, distance );\n\n },\n getWorldRotation: function () {\n\n console.error( 'THREE.Object3D: .getWorldRotation() has been removed. Use THREE.Object3D.getWorldQuaternion( target ) instead.' );\n\n }\n\n } );\n\n Object.defineProperties( Object3D.prototype, {\n\n eulerOrder: {\n get: function () {\n\n console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );\n return this.rotation.order;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.Object3D: .eulerOrder is now .rotation.order.' );\n this.rotation.order = value;\n\n }\n },\n useQuaternion: {\n get: function () {\n\n console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Object3D: .useQuaternion has been removed. The library now uses quaternions by default.' );\n\n }\n }\n\n } );\n\n Object.defineProperties( LOD.prototype, {\n\n objects: {\n get: function () {\n\n console.warn( 'THREE.LOD: .objects has been renamed to .levels.' );\n return this.levels;\n\n }\n }\n\n } );\n\n Object.defineProperty( Skeleton.prototype, 'useVertexTexture', {\n\n get: function () {\n\n console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Skeleton: useVertexTexture has been removed.' );\n\n }\n\n } );\n\n Object.defineProperty( Curve.prototype, '__arcLengthDivisions', {\n\n get: function () {\n\n console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );\n return this.arcLengthDivisions;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.Curve: .__arcLengthDivisions is now .arcLengthDivisions.' );\n this.arcLengthDivisions = value;\n\n }\n\n } );\n\n //\n\n PerspectiveCamera.prototype.setLens = function ( focalLength, filmGauge ) {\n\n console.warn( \"THREE.PerspectiveCamera.setLens is deprecated. \" +\n \"Use .setFocalLength and .filmGauge for a photographic setup.\" );\n\n if ( filmGauge !== undefined ) this.filmGauge = filmGauge;\n this.setFocalLength( focalLength );\n\n };\n\n //\n\n Object.defineProperties( Light.prototype, {\n onlyShadow: {\n set: function () {\n\n console.warn( 'THREE.Light: .onlyShadow has been removed.' );\n\n }\n },\n shadowCameraFov: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraFov is now .shadow.camera.fov.' );\n this.shadow.camera.fov = value;\n\n }\n },\n shadowCameraLeft: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraLeft is now .shadow.camera.left.' );\n this.shadow.camera.left = value;\n\n }\n },\n shadowCameraRight: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraRight is now .shadow.camera.right.' );\n this.shadow.camera.right = value;\n\n }\n },\n shadowCameraTop: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraTop is now .shadow.camera.top.' );\n this.shadow.camera.top = value;\n\n }\n },\n shadowCameraBottom: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraBottom is now .shadow.camera.bottom.' );\n this.shadow.camera.bottom = value;\n\n }\n },\n shadowCameraNear: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraNear is now .shadow.camera.near.' );\n this.shadow.camera.near = value;\n\n }\n },\n shadowCameraFar: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowCameraFar is now .shadow.camera.far.' );\n this.shadow.camera.far = value;\n\n }\n },\n shadowCameraVisible: {\n set: function () {\n\n console.warn( 'THREE.Light: .shadowCameraVisible has been removed. Use new THREE.CameraHelper( light.shadow.camera ) instead.' );\n\n }\n },\n shadowBias: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowBias is now .shadow.bias.' );\n this.shadow.bias = value;\n\n }\n },\n shadowDarkness: {\n set: function () {\n\n console.warn( 'THREE.Light: .shadowDarkness has been removed.' );\n\n }\n },\n shadowMapWidth: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowMapWidth is now .shadow.mapSize.width.' );\n this.shadow.mapSize.width = value;\n\n }\n },\n shadowMapHeight: {\n set: function ( value ) {\n\n console.warn( 'THREE.Light: .shadowMapHeight is now .shadow.mapSize.height.' );\n this.shadow.mapSize.height = value;\n\n }\n }\n } );\n\n //\n\n Object.defineProperties( BufferAttribute.prototype, {\n\n length: {\n get: function () {\n\n console.warn( 'THREE.BufferAttribute: .length has been deprecated. Use .count instead.' );\n return this.array.length;\n\n }\n },\n copyIndicesArray: function ( /* indices */ ) {\n\n console.error( 'THREE.BufferAttribute: .copyIndicesArray() has been removed.' );\n\n }\n\n } );\n\n Object.assign( BufferGeometry.prototype, {\n\n addIndex: function ( index ) {\n\n console.warn( 'THREE.BufferGeometry: .addIndex() has been renamed to .setIndex().' );\n this.setIndex( index );\n\n },\n addDrawCall: function ( start, count, indexOffset ) {\n\n if ( indexOffset !== undefined ) {\n\n console.warn( 'THREE.BufferGeometry: .addDrawCall() no longer supports indexOffset.' );\n\n }\n console.warn( 'THREE.BufferGeometry: .addDrawCall() is now .addGroup().' );\n this.addGroup( start, count );\n\n },\n clearDrawCalls: function () {\n\n console.warn( 'THREE.BufferGeometry: .clearDrawCalls() is now .clearGroups().' );\n this.clearGroups();\n\n },\n computeTangents: function () {\n\n console.warn( 'THREE.BufferGeometry: .computeTangents() has been removed.' );\n\n },\n computeOffsets: function () {\n\n console.warn( 'THREE.BufferGeometry: .computeOffsets() has been removed.' );\n\n }\n\n } );\n\n Object.defineProperties( BufferGeometry.prototype, {\n\n drawcalls: {\n get: function () {\n\n console.error( 'THREE.BufferGeometry: .drawcalls has been renamed to .groups.' );\n return this.groups;\n\n }\n },\n offsets: {\n get: function () {\n\n console.warn( 'THREE.BufferGeometry: .offsets has been renamed to .groups.' );\n return this.groups;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( Uniform.prototype, {\n\n dynamic: {\n set: function () {\n\n console.warn( 'THREE.Uniform: .dynamic has been removed. Use object.onBeforeRender() instead.' );\n\n }\n },\n onUpdate: {\n value: function () {\n\n console.warn( 'THREE.Uniform: .onUpdate() has been removed. Use object.onBeforeRender() instead.' );\n return this;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( Material.prototype, {\n\n wrapAround: {\n get: function () {\n\n console.warn( 'THREE.Material: .wrapAround has been removed.' );\n\n },\n set: function () {\n\n console.warn( 'THREE.Material: .wrapAround has been removed.' );\n\n }\n },\n wrapRGB: {\n get: function () {\n\n console.warn( 'THREE.Material: .wrapRGB has been removed.' );\n return new Color();\n\n }\n },\n\n shading: {\n get: function () {\n\n console.error( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.' + this.type + ': .shading has been removed. Use the boolean .flatShading instead.' );\n this.flatShading = ( value === FlatShading );\n\n }\n }\n\n } );\n\n Object.defineProperties( MeshPhongMaterial.prototype, {\n\n metal: {\n get: function () {\n\n console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead.' );\n return false;\n\n },\n set: function () {\n\n console.warn( 'THREE.MeshPhongMaterial: .metal has been removed. Use THREE.MeshStandardMaterial instead' );\n\n }\n }\n\n } );\n\n Object.defineProperties( ShaderMaterial.prototype, {\n\n derivatives: {\n get: function () {\n\n console.warn( 'THREE.ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );\n return this.extensions.derivatives;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE. ShaderMaterial: .derivatives has been moved to .extensions.derivatives.' );\n this.extensions.derivatives = value;\n\n }\n }\n\n } );\n\n //\n\n Object.assign( WebGLRenderer.prototype, {\n\n getCurrentRenderTarget: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getCurrentRenderTarget() is now .getRenderTarget().' );\n return this.getRenderTarget();\n\n },\n\n getMaxAnisotropy: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getMaxAnisotropy() is now .capabilities.getMaxAnisotropy().' );\n return this.capabilities.getMaxAnisotropy();\n\n },\n\n getPrecision: function () {\n\n console.warn( 'THREE.WebGLRenderer: .getPrecision() is now .capabilities.precision.' );\n return this.capabilities.precision;\n\n },\n\n resetGLState: function () {\n\n console.warn( 'THREE.WebGLRenderer: .resetGLState() is now .state.reset().' );\n return this.state.reset();\n\n },\n\n supportsFloatTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsFloatTextures() is now .extensions.get( \\'OES_texture_float\\' ).' );\n return this.extensions.get( 'OES_texture_float' );\n\n },\n supportsHalfFloatTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsHalfFloatTextures() is now .extensions.get( \\'OES_texture_half_float\\' ).' );\n return this.extensions.get( 'OES_texture_half_float' );\n\n },\n supportsStandardDerivatives: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsStandardDerivatives() is now .extensions.get( \\'OES_standard_derivatives\\' ).' );\n return this.extensions.get( 'OES_standard_derivatives' );\n\n },\n supportsCompressedTextureS3TC: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsCompressedTextureS3TC() is now .extensions.get( \\'WEBGL_compressed_texture_s3tc\\' ).' );\n return this.extensions.get( 'WEBGL_compressed_texture_s3tc' );\n\n },\n supportsCompressedTexturePVRTC: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsCompressedTexturePVRTC() is now .extensions.get( \\'WEBGL_compressed_texture_pvrtc\\' ).' );\n return this.extensions.get( 'WEBGL_compressed_texture_pvrtc' );\n\n },\n supportsBlendMinMax: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsBlendMinMax() is now .extensions.get( \\'EXT_blend_minmax\\' ).' );\n return this.extensions.get( 'EXT_blend_minmax' );\n\n },\n supportsVertexTextures: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsVertexTextures() is now .capabilities.vertexTextures.' );\n return this.capabilities.vertexTextures;\n\n },\n supportsInstancedArrays: function () {\n\n console.warn( 'THREE.WebGLRenderer: .supportsInstancedArrays() is now .extensions.get( \\'ANGLE_instanced_arrays\\' ).' );\n return this.extensions.get( 'ANGLE_instanced_arrays' );\n\n },\n enableScissorTest: function ( boolean ) {\n\n console.warn( 'THREE.WebGLRenderer: .enableScissorTest() is now .setScissorTest().' );\n this.setScissorTest( boolean );\n\n },\n initMaterial: function () {\n\n console.warn( 'THREE.WebGLRenderer: .initMaterial() has been removed.' );\n\n },\n addPrePlugin: function () {\n\n console.warn( 'THREE.WebGLRenderer: .addPrePlugin() has been removed.' );\n\n },\n addPostPlugin: function () {\n\n console.warn( 'THREE.WebGLRenderer: .addPostPlugin() has been removed.' );\n\n },\n updateShadowMap: function () {\n\n console.warn( 'THREE.WebGLRenderer: .updateShadowMap() has been removed.' );\n\n },\n setFaceCulling: function () {\n\n console.warn( 'THREE.WebGLRenderer: .setFaceCulling() has been removed.' );\n\n }\n\n } );\n\n Object.defineProperties( WebGLRenderer.prototype, {\n\n shadowMapEnabled: {\n get: function () {\n\n return this.shadowMap.enabled;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapEnabled is now .shadowMap.enabled.' );\n this.shadowMap.enabled = value;\n\n }\n },\n shadowMapType: {\n get: function () {\n\n return this.shadowMap.type;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapType is now .shadowMap.type.' );\n this.shadowMap.type = value;\n\n }\n },\n shadowMapCullFace: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function ( /* value */ ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMapCullFace has been removed. Set Material.shadowSide instead.' );\n\n }\n }\n } );\n\n Object.defineProperties( WebGLShadowMap.prototype, {\n\n cullFace: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function ( /* cullFace */ ) {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.cullFace has been removed. Set Material.shadowSide instead.' );\n\n }\n },\n renderReverseSided: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderReverseSided has been removed. Set Material.shadowSide instead.' );\n\n }\n },\n renderSingleSided: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );\n return undefined;\n\n },\n set: function () {\n\n console.warn( 'THREE.WebGLRenderer: .shadowMap.renderSingleSided has been removed. Set Material.shadowSide instead.' );\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( WebGLRenderTarget.prototype, {\n\n wrapS: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );\n return this.texture.wrapS;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapS is now .texture.wrapS.' );\n this.texture.wrapS = value;\n\n }\n },\n wrapT: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );\n return this.texture.wrapT;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .wrapT is now .texture.wrapT.' );\n this.texture.wrapT = value;\n\n }\n },\n magFilter: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );\n return this.texture.magFilter;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .magFilter is now .texture.magFilter.' );\n this.texture.magFilter = value;\n\n }\n },\n minFilter: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );\n return this.texture.minFilter;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .minFilter is now .texture.minFilter.' );\n this.texture.minFilter = value;\n\n }\n },\n anisotropy: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );\n return this.texture.anisotropy;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .anisotropy is now .texture.anisotropy.' );\n this.texture.anisotropy = value;\n\n }\n },\n offset: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );\n return this.texture.offset;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .offset is now .texture.offset.' );\n this.texture.offset = value;\n\n }\n },\n repeat: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );\n return this.texture.repeat;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .repeat is now .texture.repeat.' );\n this.texture.repeat = value;\n\n }\n },\n format: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );\n return this.texture.format;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .format is now .texture.format.' );\n this.texture.format = value;\n\n }\n },\n type: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );\n return this.texture.type;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .type is now .texture.type.' );\n this.texture.type = value;\n\n }\n },\n generateMipmaps: {\n get: function () {\n\n console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );\n return this.texture.generateMipmaps;\n\n },\n set: function ( value ) {\n\n console.warn( 'THREE.WebGLRenderTarget: .generateMipmaps is now .texture.generateMipmaps.' );\n this.texture.generateMipmaps = value;\n\n }\n }\n\n } );\n\n //\n\n Object.defineProperties( WebVRManager.prototype, {\n\n standing: {\n set: function ( /* value */ ) {\n\n console.warn( 'THREE.WebVRManager: .standing has been removed.' );\n\n }\n }\n\n } );\n\n //\n\n Audio.prototype.load = function ( file ) {\n\n console.warn( 'THREE.Audio: .load has been deprecated. Use THREE.AudioLoader instead.' );\n var scope = this;\n var audioLoader = new AudioLoader();\n audioLoader.load( file, function ( buffer ) {\n\n scope.setBuffer( buffer );\n\n } );\n return this;\n\n };\n\n AudioAnalyser.prototype.getData = function () {\n\n console.warn( 'THREE.AudioAnalyser: .getData() is now .getFrequencyData().' );\n return this.getFrequencyData();\n\n };\n\n //\n\n CubeCamera.prototype.updateCubeMap = function ( renderer, scene ) {\n\n console.warn( 'THREE.CubeCamera: .updateCubeMap() is now .update().' );\n return this.update( renderer, scene );\n\n };\n\n //\n\n var GeometryUtils = {\n\n merge: function ( geometry1, geometry2, materialIndexOffset ) {\n\n console.warn( 'THREE.GeometryUtils: .merge() has been moved to Geometry. Use geometry.merge( geometry2, matrix, materialIndexOffset ) instead.' );\n var matrix;\n\n if ( geometry2.isMesh ) {\n\n geometry2.matrixAutoUpdate && geometry2.updateMatrix();\n\n matrix = geometry2.matrix;\n geometry2 = geometry2.geometry;\n\n }\n\n geometry1.merge( geometry2, matrix, materialIndexOffset );\n\n },\n\n center: function ( geometry ) {\n\n console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );\n return geometry.center();\n\n }\n\n };\n\n var ImageUtils = {\n\n crossOrigin: undefined,\n\n loadTexture: function ( url, mapping, onLoad, onError ) {\n\n console.warn( 'THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.' );\n\n var loader = new TextureLoader();\n loader.setCrossOrigin( this.crossOrigin );\n\n var texture = loader.load( url, onLoad, undefined, onError );\n\n if ( mapping ) texture.mapping = mapping;\n\n return texture;\n\n },\n\n loadTextureCube: function ( urls, mapping, onLoad, onError ) {\n\n console.warn( 'THREE.ImageUtils.loadTextureCube has been deprecated. Use THREE.CubeTextureLoader() instead.' );\n\n var loader = new CubeTextureLoader();\n loader.setCrossOrigin( this.crossOrigin );\n\n var texture = loader.load( urls, onLoad, undefined, onError );\n\n if ( mapping ) texture.mapping = mapping;\n\n return texture;\n\n },\n\n loadCompressedTexture: function () {\n\n console.error( 'THREE.ImageUtils.loadCompressedTexture has been removed. Use THREE.DDSLoader instead.' );\n\n },\n\n loadCompressedTextureCube: function () {\n\n console.error( 'THREE.ImageUtils.loadCompressedTextureCube has been removed. Use THREE.DDSLoader instead.' );\n\n }\n\n };\n\n //\n\n function Projector() {\n\n console.error( 'THREE.Projector has been moved to /examples/js/renderers/Projector.js.' );\n\n this.projectVector = function ( vector, camera ) {\n\n console.warn( 'THREE.Projector: .projectVector() is now vector.project().' );\n vector.project( camera );\n\n };\n\n this.unprojectVector = function ( vector, camera ) {\n\n console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' );\n vector.unproject( camera );\n\n };\n\n this.pickingRay = function () {\n\n console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' );\n\n };\n\n }\n\n //\n\n function CanvasRenderer() {\n\n console.error( 'THREE.CanvasRenderer has been moved to /examples/js/renderers/CanvasRenderer.js' );\n\n this.domElement = document.createElementNS( 'http://www.w3.org/1999/xhtml', 'canvas' );\n this.clear = function () {};\n this.render = function () {};\n this.setClearColor = function () {};\n this.setSize = function () {};\n\n }\n\n //\n\n var SceneUtils = {\n\n createMultiMaterialObject: function ( /* geometry, materials */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n },\n\n detach: function ( /* child, parent, scene */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n },\n\n attach: function ( /* child, scene, parent */ ) {\n\n console.error( 'THREE.SceneUtils has been moved to /examples/js/utils/SceneUtils.js' );\n\n }\n\n };\n\n //\n\n function LensFlare() {\n\n console.error( 'THREE.LensFlare has been moved to /examples/js/objects/Lensflare.js' );\n\n }\n\n exports.WebGLRenderTargetCube = WebGLRenderTargetCube;\n exports.WebGLRenderTarget = WebGLRenderTarget;\n exports.WebGLRenderer = WebGLRenderer;\n exports.ShaderLib = ShaderLib;\n exports.UniformsLib = UniformsLib;\n exports.UniformsUtils = UniformsUtils;\n exports.ShaderChunk = ShaderChunk;\n exports.FogExp2 = FogExp2;\n exports.Fog = Fog;\n exports.Scene = Scene;\n exports.Sprite = Sprite;\n exports.LOD = LOD;\n exports.SkinnedMesh = SkinnedMesh;\n exports.Skeleton = Skeleton;\n exports.Bone = Bone;\n exports.Mesh = Mesh;\n exports.LineSegments = LineSegments;\n exports.LineLoop = LineLoop;\n exports.Line = Line;\n exports.Points = Points;\n exports.Group = Group;\n exports.VideoTexture = VideoTexture;\n exports.DataTexture = DataTexture;\n exports.CompressedTexture = CompressedTexture;\n exports.CubeTexture = CubeTexture;\n exports.CanvasTexture = CanvasTexture;\n exports.DepthTexture = DepthTexture;\n exports.Texture = Texture;\n exports.CompressedTextureLoader = CompressedTextureLoader;\n exports.DataTextureLoader = DataTextureLoader;\n exports.CubeTextureLoader = CubeTextureLoader;\n exports.TextureLoader = TextureLoader;\n exports.ObjectLoader = ObjectLoader;\n exports.MaterialLoader = MaterialLoader;\n exports.BufferGeometryLoader = BufferGeometryLoader;\n exports.DefaultLoadingManager = DefaultLoadingManager;\n exports.LoadingManager = LoadingManager;\n exports.JSONLoader = JSONLoader;\n exports.ImageLoader = ImageLoader;\n exports.ImageBitmapLoader = ImageBitmapLoader;\n exports.FontLoader = FontLoader;\n exports.FileLoader = FileLoader;\n exports.Loader = Loader;\n exports.LoaderUtils = LoaderUtils;\n exports.Cache = Cache;\n exports.AudioLoader = AudioLoader;\n exports.SpotLightShadow = SpotLightShadow;\n exports.SpotLight = SpotLight;\n exports.PointLight = PointLight;\n exports.RectAreaLight = RectAreaLight;\n exports.HemisphereLight = HemisphereLight;\n exports.DirectionalLightShadow = DirectionalLightShadow;\n exports.DirectionalLight = DirectionalLight;\n exports.AmbientLight = AmbientLight;\n exports.LightShadow = LightShadow;\n exports.Light = Light;\n exports.StereoCamera = StereoCamera;\n exports.PerspectiveCamera = PerspectiveCamera;\n exports.OrthographicCamera = OrthographicCamera;\n exports.CubeCamera = CubeCamera;\n exports.ArrayCamera = ArrayCamera;\n exports.Camera = Camera;\n exports.AudioListener = AudioListener;\n exports.PositionalAudio = PositionalAudio;\n exports.AudioContext = AudioContext;\n exports.AudioAnalyser = AudioAnalyser;\n exports.Audio = Audio;\n exports.VectorKeyframeTrack = VectorKeyframeTrack;\n exports.StringKeyframeTrack = StringKeyframeTrack;\n exports.QuaternionKeyframeTrack = QuaternionKeyframeTrack;\n exports.NumberKeyframeTrack = NumberKeyframeTrack;\n exports.ColorKeyframeTrack = ColorKeyframeTrack;\n exports.BooleanKeyframeTrack = BooleanKeyframeTrack;\n exports.PropertyMixer = PropertyMixer;\n exports.PropertyBinding = PropertyBinding;\n exports.KeyframeTrack = KeyframeTrack;\n exports.AnimationUtils = AnimationUtils;\n exports.AnimationObjectGroup = AnimationObjectGroup;\n exports.AnimationMixer = AnimationMixer;\n exports.AnimationClip = AnimationClip;\n exports.Uniform = Uniform;\n exports.InstancedBufferGeometry = InstancedBufferGeometry;\n exports.BufferGeometry = BufferGeometry;\n exports.Geometry = Geometry;\n exports.InterleavedBufferAttribute = InterleavedBufferAttribute;\n exports.InstancedInterleavedBuffer = InstancedInterleavedBuffer;\n exports.InterleavedBuffer = InterleavedBuffer;\n exports.InstancedBufferAttribute = InstancedBufferAttribute;\n exports.Face3 = Face3;\n exports.Object3D = Object3D;\n exports.Raycaster = Raycaster;\n exports.Layers = Layers;\n exports.EventDispatcher = EventDispatcher;\n exports.Clock = Clock;\n exports.QuaternionLinearInterpolant = QuaternionLinearInterpolant;\n exports.LinearInterpolant = LinearInterpolant;\n exports.DiscreteInterpolant = DiscreteInterpolant;\n exports.CubicInterpolant = CubicInterpolant;\n exports.Interpolant = Interpolant;\n exports.Triangle = Triangle;\n exports.Math = _Math;\n exports.Spherical = Spherical;\n exports.Cylindrical = Cylindrical;\n exports.Plane = Plane;\n exports.Frustum = Frustum;\n exports.Sphere = Sphere;\n exports.Ray = Ray;\n exports.Matrix4 = Matrix4;\n exports.Matrix3 = Matrix3;\n exports.Box3 = Box3;\n exports.Box2 = Box2;\n exports.Line3 = Line3;\n exports.Euler = Euler;\n exports.Vector4 = Vector4;\n exports.Vector3 = Vector3;\n exports.Vector2 = Vector2;\n exports.Quaternion = Quaternion;\n exports.Color = Color;\n exports.ImmediateRenderObject = ImmediateRenderObject;\n exports.VertexNormalsHelper = VertexNormalsHelper;\n exports.SpotLightHelper = SpotLightHelper;\n exports.SkeletonHelper = SkeletonHelper;\n exports.PointLightHelper = PointLightHelper;\n exports.RectAreaLightHelper = RectAreaLightHelper;\n exports.HemisphereLightHelper = HemisphereLightHelper;\n exports.GridHelper = GridHelper;\n exports.PolarGridHelper = PolarGridHelper;\n exports.FaceNormalsHelper = FaceNormalsHelper;\n exports.DirectionalLightHelper = DirectionalLightHelper;\n exports.CameraHelper = CameraHelper;\n exports.BoxHelper = BoxHelper;\n exports.Box3Helper = Box3Helper;\n exports.PlaneHelper = PlaneHelper;\n exports.ArrowHelper = ArrowHelper;\n exports.AxesHelper = AxesHelper;\n exports.Shape = Shape;\n exports.Path = Path;\n exports.ShapePath = ShapePath;\n exports.Font = Font;\n exports.CurvePath = CurvePath;\n exports.Curve = Curve;\n exports.ShapeUtils = ShapeUtils;\n exports.WebGLUtils = WebGLUtils;\n exports.WireframeGeometry = WireframeGeometry;\n exports.ParametricGeometry = ParametricGeometry;\n exports.ParametricBufferGeometry = ParametricBufferGeometry;\n exports.TetrahedronGeometry = TetrahedronGeometry;\n exports.TetrahedronBufferGeometry = TetrahedronBufferGeometry;\n exports.OctahedronGeometry = OctahedronGeometry;\n exports.OctahedronBufferGeometry = OctahedronBufferGeometry;\n exports.IcosahedronGeometry = IcosahedronGeometry;\n exports.IcosahedronBufferGeometry = IcosahedronBufferGeometry;\n exports.DodecahedronGeometry = DodecahedronGeometry;\n exports.DodecahedronBufferGeometry = DodecahedronBufferGeometry;\n exports.PolyhedronGeometry = PolyhedronGeometry;\n exports.PolyhedronBufferGeometry = PolyhedronBufferGeometry;\n exports.TubeGeometry = TubeGeometry;\n exports.TubeBufferGeometry = TubeBufferGeometry;\n exports.TorusKnotGeometry = TorusKnotGeometry;\n exports.TorusKnotBufferGeometry = TorusKnotBufferGeometry;\n exports.TorusGeometry = TorusGeometry;\n exports.TorusBufferGeometry = TorusBufferGeometry;\n exports.TextGeometry = TextGeometry;\n exports.TextBufferGeometry = TextBufferGeometry;\n exports.SphereGeometry = SphereGeometry;\n exports.SphereBufferGeometry = SphereBufferGeometry;\n exports.RingGeometry = RingGeometry;\n exports.RingBufferGeometry = RingBufferGeometry;\n exports.PlaneGeometry = PlaneGeometry;\n exports.PlaneBufferGeometry = PlaneBufferGeometry;\n exports.LatheGeometry = LatheGeometry;\n exports.LatheBufferGeometry = LatheBufferGeometry;\n exports.ShapeGeometry = ShapeGeometry;\n exports.ShapeBufferGeometry = ShapeBufferGeometry;\n exports.ExtrudeGeometry = ExtrudeGeometry;\n exports.ExtrudeBufferGeometry = ExtrudeBufferGeometry;\n exports.EdgesGeometry = EdgesGeometry;\n exports.ConeGeometry = ConeGeometry;\n exports.ConeBufferGeometry = ConeBufferGeometry;\n exports.CylinderGeometry = CylinderGeometry;\n exports.CylinderBufferGeometry = CylinderBufferGeometry;\n exports.CircleGeometry = CircleGeometry;\n exports.CircleBufferGeometry = CircleBufferGeometry;\n exports.BoxGeometry = BoxGeometry;\n exports.BoxBufferGeometry = BoxBufferGeometry;\n exports.ShadowMaterial = ShadowMaterial;\n exports.SpriteMaterial = SpriteMaterial;\n exports.RawShaderMaterial = RawShaderMaterial;\n exports.ShaderMaterial = ShaderMaterial;\n exports.PointsMaterial = PointsMaterial;\n exports.MeshPhysicalMaterial = MeshPhysicalMaterial;\n exports.MeshStandardMaterial = MeshStandardMaterial;\n exports.MeshPhongMaterial = MeshPhongMaterial;\n exports.MeshToonMaterial = MeshToonMaterial;\n exports.MeshNormalMaterial = MeshNormalMaterial;\n exports.MeshLambertMaterial = MeshLambertMaterial;\n exports.MeshDepthMaterial = MeshDepthMaterial;\n exports.MeshDistanceMaterial = MeshDistanceMaterial;\n exports.MeshBasicMaterial = MeshBasicMaterial;\n exports.LineDashedMaterial = LineDashedMaterial;\n exports.LineBasicMaterial = LineBasicMaterial;\n exports.Material = Material;\n exports.Float64BufferAttribute = Float64BufferAttribute;\n exports.Float32BufferAttribute = Float32BufferAttribute;\n exports.Uint32BufferAttribute = Uint32BufferAttribute;\n exports.Int32BufferAttribute = Int32BufferAttribute;\n exports.Uint16BufferAttribute = Uint16BufferAttribute;\n exports.Int16BufferAttribute = Int16BufferAttribute;\n exports.Uint8ClampedBufferAttribute = Uint8ClampedBufferAttribute;\n exports.Uint8BufferAttribute = Uint8BufferAttribute;\n exports.Int8BufferAttribute = Int8BufferAttribute;\n exports.BufferAttribute = BufferAttribute;\n exports.ArcCurve = ArcCurve;\n exports.CatmullRomCurve3 = CatmullRomCurve3;\n exports.CubicBezierCurve = CubicBezierCurve;\n exports.CubicBezierCurve3 = CubicBezierCurve3;\n exports.EllipseCurve = EllipseCurve;\n exports.LineCurve = LineCurve;\n exports.LineCurve3 = LineCurve3;\n exports.QuadraticBezierCurve = QuadraticBezierCurve;\n exports.QuadraticBezierCurve3 = QuadraticBezierCurve3;\n exports.SplineCurve = SplineCurve;\n exports.REVISION = REVISION;\n exports.MOUSE = MOUSE;\n exports.CullFaceNone = CullFaceNone;\n exports.CullFaceBack = CullFaceBack;\n exports.CullFaceFront = CullFaceFront;\n exports.CullFaceFrontBack = CullFaceFrontBack;\n exports.FrontFaceDirectionCW = FrontFaceDirectionCW;\n exports.FrontFaceDirectionCCW = FrontFaceDirectionCCW;\n exports.BasicShadowMap = BasicShadowMap;\n exports.PCFShadowMap = PCFShadowMap;\n exports.PCFSoftShadowMap = PCFSoftShadowMap;\n exports.FrontSide = FrontSide;\n exports.BackSide = BackSide;\n exports.DoubleSide = DoubleSide;\n exports.FlatShading = FlatShading;\n exports.SmoothShading = SmoothShading;\n exports.NoColors = NoColors;\n exports.FaceColors = FaceColors;\n exports.VertexColors = VertexColors;\n exports.NoBlending = NoBlending;\n exports.NormalBlending = NormalBlending;\n exports.AdditiveBlending = AdditiveBlending;\n exports.SubtractiveBlending = SubtractiveBlending;\n exports.MultiplyBlending = MultiplyBlending;\n exports.CustomBlending = CustomBlending;\n exports.AddEquation = AddEquation;\n exports.SubtractEquation = SubtractEquation;\n exports.ReverseSubtractEquation = ReverseSubtractEquation;\n exports.MinEquation = MinEquation;\n exports.MaxEquation = MaxEquation;\n exports.ZeroFactor = ZeroFactor;\n exports.OneFactor = OneFactor;\n exports.SrcColorFactor = SrcColorFactor;\n exports.OneMinusSrcColorFactor = OneMinusSrcColorFactor;\n exports.SrcAlphaFactor = SrcAlphaFactor;\n exports.OneMinusSrcAlphaFactor = OneMinusSrcAlphaFactor;\n exports.DstAlphaFactor = DstAlphaFactor;\n exports.OneMinusDstAlphaFactor = OneMinusDstAlphaFactor;\n exports.DstColorFactor = DstColorFactor;\n exports.OneMinusDstColorFactor = OneMinusDstColorFactor;\n exports.SrcAlphaSaturateFactor = SrcAlphaSaturateFactor;\n exports.NeverDepth = NeverDepth;\n exports.AlwaysDepth = AlwaysDepth;\n exports.LessDepth = LessDepth;\n exports.LessEqualDepth = LessEqualDepth;\n exports.EqualDepth = EqualDepth;\n exports.GreaterEqualDepth = GreaterEqualDepth;\n exports.GreaterDepth = GreaterDepth;\n exports.NotEqualDepth = NotEqualDepth;\n exports.MultiplyOperation = MultiplyOperation;\n exports.MixOperation = MixOperation;\n exports.AddOperation = AddOperation;\n exports.NoToneMapping = NoToneMapping;\n exports.LinearToneMapping = LinearToneMapping;\n exports.ReinhardToneMapping = ReinhardToneMapping;\n exports.Uncharted2ToneMapping = Uncharted2ToneMapping;\n exports.CineonToneMapping = CineonToneMapping;\n exports.UVMapping = UVMapping;\n exports.CubeReflectionMapping = CubeReflectionMapping;\n exports.CubeRefractionMapping = CubeRefractionMapping;\n exports.EquirectangularReflectionMapping = EquirectangularReflectionMapping;\n exports.EquirectangularRefractionMapping = EquirectangularRefractionMapping;\n exports.SphericalReflectionMapping = SphericalReflectionMapping;\n exports.CubeUVReflectionMapping = CubeUVReflectionMapping;\n exports.CubeUVRefractionMapping = CubeUVRefractionMapping;\n exports.RepeatWrapping = RepeatWrapping;\n exports.ClampToEdgeWrapping = ClampToEdgeWrapping;\n exports.MirroredRepeatWrapping = MirroredRepeatWrapping;\n exports.NearestFilter = NearestFilter;\n exports.NearestMipMapNearestFilter = NearestMipMapNearestFilter;\n exports.NearestMipMapLinearFilter = NearestMipMapLinearFilter;\n exports.LinearFilter = LinearFilter;\n exports.LinearMipMapNearestFilter = LinearMipMapNearestFilter;\n exports.LinearMipMapLinearFilter = LinearMipMapLinearFilter;\n exports.UnsignedByteType = UnsignedByteType;\n exports.ByteType = ByteType;\n exports.ShortType = ShortType;\n exports.UnsignedShortType = UnsignedShortType;\n exports.IntType = IntType;\n exports.UnsignedIntType = UnsignedIntType;\n exports.FloatType = FloatType;\n exports.HalfFloatType = HalfFloatType;\n exports.UnsignedShort4444Type = UnsignedShort4444Type;\n exports.UnsignedShort5551Type = UnsignedShort5551Type;\n exports.UnsignedShort565Type = UnsignedShort565Type;\n exports.UnsignedInt248Type = UnsignedInt248Type;\n exports.AlphaFormat = AlphaFormat;\n exports.RGBFormat = RGBFormat;\n exports.RGBAFormat = RGBAFormat;\n exports.LuminanceFormat = LuminanceFormat;\n exports.LuminanceAlphaFormat = LuminanceAlphaFormat;\n exports.RGBEFormat = RGBEFormat;\n exports.DepthFormat = DepthFormat;\n exports.DepthStencilFormat = DepthStencilFormat;\n exports.RGB_S3TC_DXT1_Format = RGB_S3TC_DXT1_Format;\n exports.RGBA_S3TC_DXT1_Format = RGBA_S3TC_DXT1_Format;\n exports.RGBA_S3TC_DXT3_Format = RGBA_S3TC_DXT3_Format;\n exports.RGBA_S3TC_DXT5_Format = RGBA_S3TC_DXT5_Format;\n exports.RGB_PVRTC_4BPPV1_Format = RGB_PVRTC_4BPPV1_Format;\n exports.RGB_PVRTC_2BPPV1_Format = RGB_PVRTC_2BPPV1_Format;\n exports.RGBA_PVRTC_4BPPV1_Format = RGBA_PVRTC_4BPPV1_Format;\n exports.RGBA_PVRTC_2BPPV1_Format = RGBA_PVRTC_2BPPV1_Format;\n exports.RGB_ETC1_Format = RGB_ETC1_Format;\n exports.RGBA_ASTC_4x4_Format = RGBA_ASTC_4x4_Format;\n exports.RGBA_ASTC_5x4_Format = RGBA_ASTC_5x4_Format;\n exports.RGBA_ASTC_5x5_Format = RGBA_ASTC_5x5_Format;\n exports.RGBA_ASTC_6x5_Format = RGBA_ASTC_6x5_Format;\n exports.RGBA_ASTC_6x6_Format = RGBA_ASTC_6x6_Format;\n exports.RGBA_ASTC_8x5_Format = RGBA_ASTC_8x5_Format;\n exports.RGBA_ASTC_8x6_Format = RGBA_ASTC_8x6_Format;\n exports.RGBA_ASTC_8x8_Format = RGBA_ASTC_8x8_Format;\n exports.RGBA_ASTC_10x5_Format = RGBA_ASTC_10x5_Format;\n exports.RGBA_ASTC_10x6_Format = RGBA_ASTC_10x6_Format;\n exports.RGBA_ASTC_10x8_Format = RGBA_ASTC_10x8_Format;\n exports.RGBA_ASTC_10x10_Format = RGBA_ASTC_10x10_Format;\n exports.RGBA_ASTC_12x10_Format = RGBA_ASTC_12x10_Format;\n exports.RGBA_ASTC_12x12_Format = RGBA_ASTC_12x12_Format;\n exports.LoopOnce = LoopOnce;\n exports.LoopRepeat = LoopRepeat;\n exports.LoopPingPong = LoopPingPong;\n exports.InterpolateDiscrete = InterpolateDiscrete;\n exports.InterpolateLinear = InterpolateLinear;\n exports.InterpolateSmooth = InterpolateSmooth;\n exports.ZeroCurvatureEnding = ZeroCurvatureEnding;\n exports.ZeroSlopeEnding = ZeroSlopeEnding;\n exports.WrapAroundEnding = WrapAroundEnding;\n exports.TrianglesDrawMode = TrianglesDrawMode;\n exports.TriangleStripDrawMode = TriangleStripDrawMode;\n exports.TriangleFanDrawMode = TriangleFanDrawMode;\n exports.LinearEncoding = LinearEncoding;\n exports.sRGBEncoding = sRGBEncoding;\n exports.GammaEncoding = GammaEncoding;\n exports.RGBEEncoding = RGBEEncoding;\n exports.LogLuvEncoding = LogLuvEncoding;\n exports.RGBM7Encoding = RGBM7Encoding;\n exports.RGBM16Encoding = RGBM16Encoding;\n exports.RGBDEncoding = RGBDEncoding;\n exports.BasicDepthPacking = BasicDepthPacking;\n exports.RGBADepthPacking = RGBADepthPacking;\n exports.CubeGeometry = BoxGeometry;\n exports.Face4 = Face4;\n exports.LineStrip = LineStrip;\n exports.LinePieces = LinePieces;\n exports.MeshFaceMaterial = MeshFaceMaterial;\n exports.MultiMaterial = MultiMaterial;\n exports.PointCloud = PointCloud;\n exports.Particle = Particle;\n exports.ParticleSystem = ParticleSystem;\n exports.PointCloudMaterial = PointCloudMaterial;\n exports.ParticleBasicMaterial = ParticleBasicMaterial;\n exports.ParticleSystemMaterial = ParticleSystemMaterial;\n exports.Vertex = Vertex;\n exports.DynamicBufferAttribute = DynamicBufferAttribute;\n exports.Int8Attribute = Int8Attribute;\n exports.Uint8Attribute = Uint8Attribute;\n exports.Uint8ClampedAttribute = Uint8ClampedAttribute;\n exports.Int16Attribute = Int16Attribute;\n exports.Uint16Attribute = Uint16Attribute;\n exports.Int32Attribute = Int32Attribute;\n exports.Uint32Attribute = Uint32Attribute;\n exports.Float32Attribute = Float32Attribute;\n exports.Float64Attribute = Float64Attribute;\n exports.ClosedSplineCurve3 = ClosedSplineCurve3;\n exports.SplineCurve3 = SplineCurve3;\n exports.Spline = Spline;\n exports.AxisHelper = AxisHelper;\n exports.BoundingBoxHelper = BoundingBoxHelper;\n exports.EdgesHelper = EdgesHelper;\n exports.WireframeHelper = WireframeHelper;\n exports.XHRLoader = XHRLoader;\n exports.BinaryTextureLoader = BinaryTextureLoader;\n exports.GeometryUtils = GeometryUtils;\n exports.ImageUtils = ImageUtils;\n exports.Projector = Projector;\n exports.CanvasRenderer = CanvasRenderer;\n exports.SceneUtils = SceneUtils;\n exports.LensFlare = LensFlare;\n\n Object.defineProperty(exports, '__esModule', { value: true });\n\n})));\n`\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1455.1229188846069","left":"2165.293772061664","inputs":{},"outputs":{}},"0.5881562772156042":{"definition":"//\n// view path\n//\n// Neil Gershenfeld \n// (c) Massachusetts Institute of Technology 2016\n// \n// This work may be reproduced, modified, distributed, performed, and \n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is \n// provided as is; no warranty is provided, and users accept all \n// liability.\n//\n// todo:\n// erase and update new path\n// show depth info\n// show size\n// calculate camera far\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'view path'\n//\n// initialization\n//\nvar init = function() {\n }\n//\n// inputs\n//\nvar inputs = {\n path:{type:'',\n event:function(evt){\n mod.path = evt.detail.path\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n mod.depth = evt.detail.depth\n show_path_info()\n show_path()\n outputs.path.event()\n }},\n response:{type:'three.js',\n event:function(evt){\n var script = document.createElement('script')\n //script.type = 'text/javascript'\n script.onload = init_window\n script.src = 'data:text/javascript,' + encodeURIComponent(evt.detail)\n mod.div.appendChild(script)\n //init_window(evt.detail)\n }}}\n//\n// outputs\n//\nvar outputs = {\n path:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.path\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n mods.output(mod,'path',cmd)\n }},\n request:{type:'three.js',\n event:function(arg){\n mods.output(mod,'request',arg)\n }}}\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // info\n //\n var text = document.createTextNode('name: ')\n div.appendChild(text)\n mod.nametext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(mm)')\n div.appendChild(text)\n mod.mmtext = text\n div.appendChild(document.createElement('br'))\n var text = document.createTextNode('(in)')\n div.appendChild(text)\n mod.intext = text\n //\n // view\n // \n div.appendChild(document.createElement('br')) \n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('view')\n span.appendChild(text)\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n open_view_window()\n })\n div.appendChild(btn)\n }\n//\n// local functions\n//\n// show_path_info\n//\nfunction show_path_info() {\n mod.nametext.nodeValue = 'name: '+mod.name\n var width = (25.4*mod.width/mod.dpi).toFixed(3)\n var height = (25.4*mod.height/mod.dpi).toFixed(3)\n var depth = (25.4*mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.mmtext.nodeValue = width+' x '+height+' (mm)'\n else\n mod.mmtext.nodeValue = width+' x '+height+' x '+depth+' (mm)'\n var width = (mod.width/mod.dpi).toFixed(3)\n var height = (mod.height/mod.dpi).toFixed(3)\n var depth = (mod.depth/mod.dpi).toFixed(3)\n if (mod.depth == undefined)\n mod.intext.nodeValue = width+' x '+height+' (in)'\n else\n mod.intext.nodeValue = width+' x '+height+' x '+depth+' (in)'\n mods.fit(mod.div)\n }\n//\n// show_path\n//\nfunction show_path() {\n var scene = mod.scene\n var camera = mod.camera\n var renderer = mod.renderer\n //\n // check if view window open\n //\n if (mod.win == undefined) {\n open_view_window()\n return\n }\n //\n // check for path\n //\n if (mod.path == undefined)\n return\n //\n // clear scene, leave camera\n //\n var length = scene.children.length\n for (var c = (length-1); c > 1; --c) {\n scene.remove(scene.children[c])\n }\n //\n // fit camera\n //\n mod.thetaxy = 0\n mod.thetaz = 0\n mod.r = mod.height/2\n mod.x0 = mod.width/2\n mod.y0 = mod.height/2\n camera.position.set(mod.x0,mod.y0,mod.r)\n camera.up = new THREE.Vector3(0,1,0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n //\n // draw segments\n //\n var arrow_size = 1+mod.width/200\n var path = mod.path\n for (var segment = 0; segment < path.length; ++segment) {\n if (segment > 0)\n add_arrow(path[segment-1][path[segment-1].length-1],path[segment][0],0xff0000,arrow_size) \n for (var point = 1; point < path[segment].length; ++point) {\n add_arrow(path[segment][point-1],path[segment][point],0x0000ff,arrow_size)\n }\n }\n //\n // add axes\n //\n var length = mod.height/10\n add_arrow([0,0,0],[length,0,0],0xff0000,arrow_size)\n add_arrow([0,0,0],[0,length,0],0x00ff00,arrow_size)\n add_arrow([0,0,0],[0,0,length],0x0000ff,arrow_size)\n //\n // render\n //\n update()\n //\n // add_arrow\n //\n function add_arrow(start,stop,color,size) {\n var origin = new THREE.Vector3().fromArray(start)\n if (mod.depth == undefined)\n origin.z = 0\n var end = new THREE.Vector3().fromArray(stop)\n if (mod.depth == undefined)\n end.z = 0\n var length = new THREE.Vector3().subVectors(end,origin).length()\n if (length <= size) {\n add_line(origin,end,color)\n //length = 1.1*size\n return\n }\n var direction = new THREE.Vector3().subVectors(end,origin).normalize()\n var arrow = new THREE.ArrowHelper(direction,origin,length,color,size,size)\n scene.add(arrow)\n }\n //\n // add_line\n //\n function add_line(start,stop,colorhex) {\n var geometry = new THREE.Geometry()\n geometry.vertices.push(start,stop)\n var material = new THREE.LineBasicMaterial({color:colorhex})\n var line = new THREE.Line(geometry,material)\n scene.add(line)\n }\n //\n // update\n //\n function update() {\n renderer.render(scene,camera)\n }\n }\n//\n// open_view_window\n//\nfunction open_view_window() {\n //\n // open window\n //\n win = window.open('')\n mod.win = win\n //\n // load three.js\n //\n outputs.request.event('three.js')\n }\n//\n// init_window\n//\nfunction init_window() {\n //document.write('<script type=\"text/javascript\">'+arg+'</script>')\n //document.close()\n //\n // close button\n //\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n mod.win.close()\n mod.win = undefined\n })\n mod.win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n mod.win.document.body.appendChild(text)\n //\n // GL container\n //\n mod.win.document.body.appendChild(document.createElement('br')) \n container = mod.win.document.createElement('div')\n container.style.overflow = 'hidden'\n mod.win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = mod.win.innerWidth\n var height = mod.win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n //\n // context_menu\n //\n function context_menu(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n return (false)\n }\n //\n // mouse_down\n //\n function mouse_down(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n mod.button = evt.button\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_up\n //\n function mouse_up(evt) {\n mod.button = undefined\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_move\n //\n function mouse_move(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dx = evt.clientX-mod.x\n var dy = evt.clientY-mod.y\n mod.x = evt.clientX\n mod.y = evt.clientY\n if (mod.button == 0) {\n mod.x0 += \n Math.sin(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/mod.win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/mod.win.innerHeight\n mod.thetaz += dx/mod.win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/mod.win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// old_open_view_window\n//\nfunction old_open_view_window() {\n //\n // globals\n //\n var container,scene,camera,renderer,win,controls\n //\n // open the window\n //\n open_window()\n //\n // open_window\n //\n function open_window() {\n //\n // open window\n //\n win = window.open('')\n mod.win = win\n //\n // load three.js\n //\n var script = document.createElement('script')\n script.type = 'text/javascript'\n script.onload = init_window\n script.src = 'js/three.js/three.min.js'\n mod.div.appendChild(script)\n }\n //\n // init_window\n //\n function init_window() {\n //\n // close button\n //\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n mod.win = undefined\n })\n win.document.body.appendChild(btn)\n //\n // label text\n //\n var text = win.document.createTextNode(' left: pan, right: rotate, scroll: zoom')\n win.document.body.appendChild(text)\n //\n // GL container\n //\n win.document.body.appendChild(document.createElement('br')) \n container = win.document.createElement('div')\n container.style.overflow = 'hidden'\n win.document.body.appendChild(container)\n //\n // event handlers\n //\n container.addEventListener('contextmenu',context_menu)\n container.addEventListener('mousedown',mouse_down)\n container.addEventListener('mouseup',mouse_up)\n container.addEventListener('mousemove',mouse_move)\n container.addEventListener('wheel',mouse_wheel)\n //\n // add scene\n //\n scene = new THREE.Scene()\n mod.scene = scene\n var width = win.innerWidth\n var height = win.innerHeight\n var aspect = width/height\n var near = 0.1\n var far = 1000000\n camera = new THREE.PerspectiveCamera(90,aspect,near,far)\n mod.camera = camera\n scene.add(camera)\n //\n // add renderer\n //\n renderer = new THREE.WebGLRenderer({antialias:true})\n mod.renderer = renderer\n renderer.setClearColor(0xffffff)\n renderer.setSize(width,height)\n container.appendChild(renderer.domElement)\n //\n // show the path if available\n //\n show_path()\n }\n //\n // context_menu\n //\n function context_menu(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n return (false)\n }\n //\n // mouse_down\n //\n function mouse_down(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n mod.button = evt.button\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_up\n //\n function mouse_up(evt) {\n mod.button = undefined\n mod.x = evt.clientX\n mod.y = evt.clientY\n }\n //\n // mouse_move\n //\n function mouse_move(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dx = evt.clientX-mod.x\n var dy = evt.clientY-mod.y\n mod.x = evt.clientX\n mod.y = evt.clientY\n if (mod.button == 0) {\n mod.x0 += \n Math.sin(mod.thetaz)*mod.height*dy/win.innerHeight\n -Math.cos(mod.thetaz)*mod.width*dx/win.innerWidth\n mod.y0 += \n Math.cos(mod.thetaz)*mod.height*dy/win.innerHeight\n +Math.sin(mod.thetaz)*mod.width*dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n else if (mod.button == 2) {\n mod.thetaxy += dy/win.innerHeight\n mod.thetaz += dx/win.innerWidth\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.up = new THREE.Vector3(Math.sin(mod.thetaz),Math.cos(mod.thetaz),0)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n //\n // mouse_wheel\n //\n function mouse_wheel(evt) {\n evt.preventDefault()\n evt.stopPropagation()\n var dy = evt.deltaY/win.innerHeight\n mod.r += mod.height*dy\n camera.position.x = mod.x0+Math.sin(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.y = mod.y0+Math.cos(mod.thetaz)*mod.r*Math.sin(mod.thetaxy)\n camera.position.z = mod.r*Math.cos(mod.thetaxy)\n camera.lookAt(new THREE.Vector3(mod.x0,mod.y0,0))\n camera.updateProjectionMatrix()\n renderer.render(scene,camera)\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n","top":"1243.5329774608936","left":"2187.1353144021764","inputs":{},"outputs":{}},"0.726998031175597":{"definition":"//\n// mill raster 2.5D\n//\n// Neil Gershenfeld\n// (c) Massachusetts Institute of Technology 2019\n//\n// This work may be reproduced, modified, distributed, performed, and\n// displayed for any purpose, but must acknowledge the mods\n// project. Copyright is retained and must be preserved. The work is\n// provided as is; no warranty is provided, and users accept all\n// liability.\n//\n// closure\n//\n(function(){\n//\n// module globals\n//\nvar mod = {}\n//\n// name\n//\nvar name = 'mill raster 2.5D'\n//\n// initialization\n//\nvar init = function() {\n mod.dia_in.value = '1'\n mod.dia_mm.value = '25.4'\n mod.cut_in.value = '0.25'\n mod.cut_mm.value = '6.35'\n mod.max_in.value = '5'\n mod.max_mm.value = '127'\n mod.number.value = '0'\n mod.stepover.value = '0.5'\n mod.merge.value = '1'\n mod.sort.checked = true\n }\n//\n// inputs\n//\nvar inputs = {\n imageInfo:{type:'',\n event:function(evt){\n mod.name = evt.detail.name\n mod.dpi = evt.detail.dpi\n mod.width = evt.detail.width\n mod.height = evt.detail.height\n var ctx = mod.img.getContext(\"2d\")\n ctx.canvas.width = mod.width\n ctx.canvas.height = mod.height\n }},\n path:{type:'',\n event:function(evt){\n if (mod.label.nodeValue == 'calculating') {\n //\n // calculation in progress, draw and accumulate\n //\n draw_path(evt.detail)\n accumulate_path(evt.detail)\n mod.offsetCount += 1\n if ((mod.offsetCount != parseInt(mod.number.value))\n && (evt.detail.length > 0)) {\n //\n // layer detail present and offset not complete\n //\n mod.offset += parseFloat(mod.stepover.value)\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else if (mod.depthmm < parseFloat(mod.max_mm.value)) {\n //\n // layer loop not complete\n //\n merge_layer()\n accumulate_toolpath()\n clear_layer()\n mod.depthmm += parseFloat(mod.cut_mm.value)\n if (mod.depthmm > parseFloat(mod.max_mm.value)) {\n mod.depthmm = parseFloat(mod.max_mm.value)\n }\n //\n // clear offset\n //\n outputs.offset.event('')\n //\n // set new depth\n //\n outputs.depth.event(mod.depthmm)\n //\n // set new offset\n //\n mod.offsetCount = 0\n outputs.offset.event(\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n }\n else {\n //\n // done, finish and output\n //\n draw_path(mod.toolpath)\n draw_connections(mod.toolpath)\n mod.label.nodeValue = 'calculate'\n mod.labelspan.style.fontWeight = 'normal'\n outputs.toolpath.event()\n }\n }\n }\n },\n settings:{type:'',\n event:function(evt){\n set_values(evt.detail)\n }\n }\n }\n//\n// outputs\n//\nvar outputs = {\n depth:{type:'',\n event:function(depth){\n mods.output(mod,'depth',{depthmm:depth})\n }\n },\n diameter:{type:'',\n event:function(){\n mods.output(mod,'diameter',Math.ceil(mod.dpi*mod.dia_in.value))\n }\n },\n offset:{type:'',\n event:function(size){\n mods.output(mod,'offset',size)\n }\n },\n toolpath:{type:'',\n event:function(){\n cmd = {}\n cmd.path = mod.toolpath\n cmd.name = mod.name\n cmd.dpi = mod.dpi\n cmd.width = mod.width\n cmd.height = mod.height\n cmd.depth = Math.round(parseFloat(mod.max_in.value)*mod.dpi)\n mods.output(mod,'toolpath',cmd)\n }\n }\n }\n//\n// interface\n//\nvar interface = function(div){\n mod.div = div\n //\n // tool diameter\n //\n div.appendChild(document.createTextNode('tool diameter'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.dia_in.value = parseFloat(mod.dia_mm.value)/25.4\n })\n div.appendChild(input)\n mod.dia_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.dia_mm.value = parseFloat(mod.dia_in.value)*25.4\n })\n div.appendChild(input)\n mod.dia_in = input\n div.appendChild(document.createElement('br'))\n //\n // cut depth\n //\n div.appendChild(document.createTextNode('cut depth'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.cut_in.value = parseFloat(mod.cut_mm.value)/25.4\n })\n div.appendChild(input)\n mod.cut_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.cut_mm.value = parseFloat(mod.cut_in.value)*25.4\n })\n div.appendChild(input)\n mod.cut_in = input\n div.appendChild(document.createElement('br'))\n //\n // max depth\n //\n div.appendChild(document.createTextNode('max depth'))\n div.appendChild(document.createElement('br'))\n div.appendChild(document.createTextNode('mm: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.max_in.value = parseFloat(mod.max_mm.value)/25.4\n })\n div.appendChild(input)\n mod.max_mm = input\n div.appendChild(document.createTextNode(' in: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n input.addEventListener('input',function(){\n mod.max_mm.value = parseFloat(mod.max_in.value)*25.4\n })\n div.appendChild(input)\n mod.max_in = input\n div.appendChild(document.createElement('br'))\n //\n // offset number\n //\n div.appendChild(document.createTextNode('offset number: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.number = input\n div.appendChild(document.createTextNode(' (0 = fill)'))\n div.appendChild(document.createElement('br'))\n //\n // offset stepover\n //\n div.appendChild(document.createTextNode('offset stepover: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.stepover = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // direction\n //\n div.appendChild(document.createTextNode('direction: '))\n div.appendChild(document.createTextNode('climb'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'direction'\n input.id = mod.div.id+'climb'\n input.checked = true\n div.appendChild(input)\n mod.climb = input\n div.appendChild(document.createTextNode(' conventional'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'direction'\n input.id = mod.div.id+'conventional'\n div.appendChild(input)\n mod.conventional = input\n div.appendChild(document.createElement('br'))\n //\n // path merge\n //\n div.appendChild(document.createTextNode('path merge: '))\n var input = document.createElement('input')\n input.type = 'text'\n input.size = 6\n div.appendChild(input)\n mod.merge = input\n div.appendChild(document.createTextNode(' (1 = diameter)'))\n div.appendChild(document.createElement('br'))\n //\n // path order\n //\n div.appendChild(document.createTextNode('path order: '))\n div.appendChild(document.createTextNode('forward'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'order'\n input.id = mod.div.id+'forward'\n input.checked = true\n div.appendChild(input)\n mod.forward = input\n div.appendChild(document.createTextNode(' reverse'))\n var input = document.createElement('input')\n input.type = 'radio'\n input.name = mod.div.id+'order'\n input.id = mod.div.id+'reverse'\n div.appendChild(input)\n mod.reverse = input\n div.appendChild(document.createElement('br'))\n //\n // sort distance\n //\n div.appendChild(document.createTextNode('sort distance: '))\n var input = document.createElement('input')\n input.type = 'checkbox'\n input.id = mod.div.id+'sort'\n div.appendChild(input)\n mod.sort = input\n div.appendChild(document.createElement('br'))\n //\n // calculate\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n var span = document.createElement('span')\n var text = document.createTextNode('calculate')\n mod.label = text\n span.appendChild(text)\n mod.labelspan = span\n btn.appendChild(span)\n btn.addEventListener('click',function(){\n mod.label.nodeValue = 'calculating'\n mod.labelspan.style.fontWeight = 'bold'\n outputs.offset.event('') // clear offset\n mod.depthmm = parseFloat(mod.cut_mm.value)\n outputs.depth.event(mod.depthmm) // set depth\n mod.toolpath = [] // start new toolpath\n clear_layer() // clear layer\n outputs.diameter.event()\n outputs.offset.event( // set offset\n mod.offset*parseFloat(mod.dia_in.value)*mod.dpi)\n })\n div.appendChild(btn)\n div.appendChild(document.createTextNode(' '))\n //\n // view\n //\n var btn = document.createElement('button')\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.appendChild(document.createTextNode('view'))\n btn.addEventListener('click',function(){\n var win = window.open('')\n var btn = document.createElement('button')\n btn.appendChild(document.createTextNode('close'))\n btn.style.padding = mods.ui.padding\n btn.style.margin = 1\n btn.addEventListener('click',function(){\n win.close()\n })\n win.document.body.appendChild(btn)\n win.document.body.appendChild(document.createElement('br'))\n var svg = document.getElementById(mod.div.id+'svg')\n var clone = svg.cloneNode(true)\n clone.setAttribute('width',mod.img.width)\n clone.setAttribute('height',mod.img.height)\n win.document.body.appendChild(clone)\n })\n div.appendChild(btn)\n div.appendChild(document.createElement('br'))\n //\n // on-screen SVG\n //\n var svgNS = \"http://www.w3.org/2000/svg\"\n var svg = document.createElementNS(svgNS,\"svg\")\n svg.setAttribute('id',mod.div.id+'svg')\n svg.setAttributeNS(\"http://www.w3.org/2000/xmlns/\",\n \"xmlns:xlink\",\"http://www.w3.org/1999/xlink\")\n svg.setAttribute('width',mods.ui.canvas)\n svg.setAttribute('height',mods.ui.canvas)\n svg.style.backgroundColor = 'rgb(255,255,255)'\n var g = document.createElementNS(svgNS,'g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n div.appendChild(svg)\n div.appendChild(document.createElement('br'))\n //\n // off-screen image canvas\n //\n var canvas = document.createElement('canvas')\n mod.img = canvas\n }\n//\n// local functions\n//\n// set_values\n//\nfunction set_values(settings) {\n for (var s in settings) {\n switch(s) {\n case 'tool diameter (in)':\n mod.dia_in.value = settings[s]\n mod.dia_mm.value = parseFloat(mod.dia_in.value)*25.4\n break\n case 'cut depth (in)':\n mod.cut_in.value = settings[s]\n mod.cut_mm.value = parseFloat(mod.cut_in.value)*25.4\n break\n case 'max depth (in)':\n mod.max_in.value = settings[s]\n mod.max_mm.value = parseFloat(mod.max_in.value)*25.4\n break\n case 'offset number':\n mod.number.value = settings[s]\n break\n }\n }\n }\n//\n// clear_layer\n//\nfunction clear_layer() {\n mod.path = []\n mod.offset = 0.5\n mod.offsetCount = 0\n var svg = document.getElementById(mod.div.id+'svg')\n svg.setAttribute(\n 'viewBox',\"0 0 \"+(mod.img.width-1)+\" \"+(mod.img.height-1))\n var g = document.getElementById(mod.div.id+'g')\n svg.removeChild(g)\n var g = document.createElementNS('http://www.w3.org/2000/svg','g')\n g.setAttribute('id',mod.div.id+'g')\n svg.appendChild(g)\n }\n//\n// accumulate_path\n// todo: replace inefficient insertion sort\n// todo: move sort out of main thread\n//\nfunction accumulate_path(path) {\n var forward = mod.forward.checked\n var conventional = mod.conventional.checked\n var sort = mod.sort.checked\n for (var segnew = 0; segnew < path.length; ++segnew) {\n if (conventional)\n path[segnew].reverse()\n if (mod.path.length == 0)\n mod.path.splice(0,0,path[segnew])\n else if (sort) {\n var xnew = path[segnew][0][0]\n var ynew = path[segnew][0][1]\n var dmin = Number.MAX_VALUE\n var segmin = -1\n for (var segold = 0; segold < mod.path.length; ++segold) {\n var xold = mod.path[segold][0][0]\n var yold = mod.path[segold][0][1]\n var dx = xnew-xold\n var dy = ynew-yold\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d < dmin) {\n dmin = d\n segmin = segold\n }\n }\n if (forward)\n mod.path.splice(segmin+1,0,path[segnew])\n else\n mod.path.splice(segmin,0,path[segnew])\n }\n else {\n if (forward)\n mod.path.splice(mod.path.length,0,path[segnew])\n else\n mod.path.splice(0,0,path[segnew])\n }\n }\n }\n//\n// merge_layer\n//\nfunction merge_layer() {\n var dmerge = mod.dpi*parseFloat(mod.merge.value)*parseFloat(mod.dia_in.value)\n var seg = 0\n while (seg < (mod.path.length-1)) {\n var xold = mod.path[seg][mod.path[seg].length-1][0]\n var yold = mod.path[seg][mod.path[seg].length-1][1]\n var xnew = mod.path[seg+1][0][0]\n var ynew = mod.path[seg+1][0][1]\n var dx = xnew-xold\n var dy = ynew-yold\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d < dmerge)\n mod.path.splice(seg,2,mod.path[seg].concat(mod.path[seg+1]))\n else\n seg += 1\n }\n }\n//\n// accumulate_toolpath\n//\nfunction accumulate_toolpath() {\n for (var seg = 0; seg < mod.path.length; ++seg) {\n var newseg = []\n for (var pt = 0; pt < mod.path[seg].length; ++pt) {\n var idepth = -Math.round(mod.dpi*mod.depthmm/25.4)\n var point = mod.path[seg][pt].concat(idepth)\n newseg.splice(newseg.length,0,point)\n }\n mod.toolpath.splice(mod.toolpath.legnth,0,newseg)\n }\n }\n//\n// draw_path\n//\nfunction draw_path(path) {\n var g = document.getElementById(mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n var xend = null\n var yend = null\n //\n // loop over segments\n //\n for (var segment = 0; segment < path.length; ++segment) {\n if (path[segment].length > 1) {\n //\n // loop over points\n //\n for (var point = 1; point < path[segment].length; ++point) {\n var line = document.createElementNS(\n 'http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','black')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment][point-1][0]\n var y1 = h-path[segment][point-1][1]-1\n var x2 = path[segment][point][0]\n var y2 = h-path[segment][point][1]-1\n xend = x2\n yend = y2\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n 'points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','black')\n g.appendChild(triangle)\n }\n }\n }\n }\n }\n//\n// draw_connections\n//\nfunction draw_connections(path) {\n var g = document.getElementById(mod.div.id+'g')\n var h = mod.img.height\n var w = mod.img.width\n //\n // loop over segments\n //\n for (var segment = 1; segment < path.length; ++segment) {\n //\n // draw connection from previous segment\n //\n var line = document.createElementNS(\n 'http://www.w3.org/2000/svg','line')\n line.setAttribute('stroke','red')\n line.setAttribute('stroke-width',1)\n line.setAttribute('stroke-linecap','round')\n var x1 = path[segment-1][path[segment-1].length-1][0]\n var y1 = h-path[segment-1][path[segment-1].length-1][1]-1\n var x2 = path[segment][0][0]\n var y2 = h-path[segment][0][1]-1\n line.setAttribute('x1',x1)\n line.setAttribute('y1',y1)\n line.setAttribute('x2',x2)\n line.setAttribute('y2',y2)\n var dx = x2-x1\n var dy = y2-y1\n var d = Math.sqrt(dx*dx+dy*dy)\n if (d > 0) {\n nx = 6*dx/d\n ny = 6*dy/d\n var tx = 3*dy/d\n var ty = -3*dx/d\n g.appendChild(line)\n triangle = document.createElementNS(\n 'http://www.w3.org/2000/svg','polygon')\n triangle.setAttribute(\n 'points',x2+','+y2+' '+(x2-nx+tx)+','+(y2-ny+ty)\n +' '+(x2-nx-tx)+','+(y2-ny-ty))\n triangle.setAttribute('fill','red')\n g.appendChild(triangle)\n }\n }\n }\n//\n// return values\n//\nreturn ({\n mod:mod,\n name:name,\n init:init,\n inputs:inputs,\n outputs:outputs,\n interface:interface\n })\n}())\n\n","top":"432.6650099324413","left":"2184.4067477397416","inputs":{},"outputs":{}}},"links":["{\"source\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8903773266711255\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.7562574507163453\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"file\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.4793941661670936\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"file\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.8910984899438215\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.3040697193095865\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"mesh\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"mesh\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.6488303557466412\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.47383876715576023\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"distances\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"distances\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"image\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.07944144280928633\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"image\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5086051394329043\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"response\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"response\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"request\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5086051394329043\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"request\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"imageInfo\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.749132408760488\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"path\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"depth\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.20905178335446428\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"settings\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"offset\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5791854769792683\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"offset\\\"}\"}","{\"source\":\"{\\\"id\\\":\\\"0.726998031175597\\\",\\\"type\\\":\\\"outputs\\\",\\\"name\\\":\\\"toolpath\\\"}\",\"dest\":\"{\\\"id\\\":\\\"0.5881562772156042\\\",\\\"type\\\":\\\"inputs\\\",\\\"name\\\":\\\"path\\\"}\"}"]} \ No newline at end of file