Last active
August 22, 2016 03:13
-
-
Save 1wheel/822cf24522a52078a03f to your computer and use it in GitHub Desktop.
shader gear
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
gistup |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
uniform vec2 u_resolution; | |
uniform float u_time; | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
gl_FragColor=vec4(st.x,st.y,0.0,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision highp float; | |
#endif | |
#define PI 3.14159265359 | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
// Plot a line on Y using a value between 0.0-1.0 | |
float plot(vec2 st, float pct){ | |
return smoothstep( pct-0.02, pct, st.y) - | |
smoothstep( pct, pct+0.02, st.y); | |
} | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float mx = u_mouse.x/u_resolution.x; | |
// float y = pow(st.x, abs(sin(mx*4.0 + 0.2))); | |
float y = sin(st.x*mx*100.0 + u_time)*.5 + .5; | |
// float y = step(0.5,st.x*1.0);; | |
// float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x); | |
vec3 color = vec3(y); | |
// Plot a line | |
float pct = plot(st,y); | |
color = (1.0-pct)*color+pct*vec3(0.0,1.0,0.0); | |
gl_FragColor = vec4(color,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float pct = 0.0; | |
// a. The DISTANCE from the pixel to the center | |
pct = distance(st, vec2(0.5)); | |
// vec3 color = vec3(smoothstep(.8, .9, 1.0 - pct)); | |
vec3 color = vec3(step(.1*sin(u_time) + .4,pct)); | |
gl_FragColor = vec4( color, 1.0 ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float pct = 0.0; | |
// a. The DISTANCE from the pixel to the center | |
pct = distance(st,vec2(0.4)) * distance(st,vec2(0.6)); | |
// vec3 color = vec3(smoothstep(.8, .9, 1.0 - pct)); | |
vec3 color = vec3(step(cos(u_time*.8)*.05 + .06, pct)); | |
pct = distance(st, vec2(cos(u_time)*.5 + .5, sin(u_time)*.5 + .5)); | |
color = color*vec3(step(.1, pct)); | |
gl_FragColor = vec4( color, 1.0 ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
vec3 color = vec3(0.0); | |
vec2 pos = vec2(0.5)-st; | |
float r = length(pos)*2.0; | |
float a = atan(pos.y,pos.x) + u_time/2.2; | |
float f = cos(a*3.); | |
f = abs(cos(a*3.)); | |
f = abs(cos(a*2.5))*.5+.3; | |
f = abs(cos(a*12.)*sin(a*3.))*.8+.1; | |
f = smoothstep(-.5,1., cos(a*10.))*0.2+0.5; | |
color = vec3( 1.-smoothstep(f,f+0.02,r) ); | |
// a. The DISTANCE from the pixel to the center | |
float pct = distance(st, vec2(0.5)); | |
color = color*floor(pct + .8); | |
gl_FragColor = vec4(color, 1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
#define PI 3.14159265359 | |
#define TWO_PI 6.28318530718 | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
// Reference to | |
// http://thndl.com/square-shaped-shaders.html | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
st.x *= u_resolution.x/u_resolution.y; | |
vec3 color = vec3(0.0); | |
float d = 0.0; | |
// Remap the space to -1. to 1. | |
st = st *2.-1.; | |
// Number of sides of your shape | |
float N = 3.0 + u_time/1.0; | |
// Angle and radius from the current pixel | |
float a = atan(st.x,st.y)+PI; | |
float r = TWO_PI/N; | |
// Shaping function that modulate the distance | |
d = cos(floor(.5+a/r)*r-a)*length(st); | |
color = vec3(1.0-smoothstep(.4,.41,d)); | |
// color = vec3(d); | |
gl_FragColor = vec4(color,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float mx = u_mouse.x/u_resolution.x; | |
float my = u_mouse.y/u_resolution.y; | |
gl_FragColor = vec4(st.x,my,mx,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform float u_time; | |
vec3 colorA = vec3(0.149,0.141,0.912); | |
vec3 colorB = vec3(1.000,0.833,0.224); | |
vec3 colorC = vec3(.000,0.533,0.224); | |
void main() { | |
vec3 color = vec3(0.0); | |
float pct0 = sin(u_time/3.3)*.5 + .5; | |
float pct1 = sin(u_time/9.1)*.5 + .5; | |
float pct2 = sin(u_time/6.1)*.5 + .5; | |
// Mix uses pct (a value from 0-1) to | |
// mix the two colors | |
color = mix(colorA, colorB, vec3(pct0, pct1, pct2)); | |
gl_FragColor = vec4(color,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
#define PI 3.14159265359 | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
vec3 orange = vec3(236.0,62.0,29.0)/255.0; | |
vec3 blue = vec3(79.0,102.0,177.0)/255.0; | |
vec3 yellow = vec3(248.0,227.0,111.0)/255.0; | |
float plot (vec2 st, float pct){ | |
return smoothstep( pct-0.01, pct, st.y) - | |
smoothstep( pct, pct+0.01, st.y); | |
} | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
vec3 color = vec3(0.0); | |
vec3 pct = vec3(st.x)*cos(u_time/4.0); | |
// pct.r = smoothstep(0.0,1.0, st.x); | |
// pct.g = sin(st.x*PI); | |
// pct.b = pow(st.x,0.5); | |
color = mix(blue, orange, pct); | |
color = mix(color, yellow, st.y*sin(u_time/5.0) + sin(u_time/10.0)/10.0); | |
// Plot transition lines for each channel | |
// color = mix(color,vec3(1.0,0.0,0.0),plot(st,pct.r)); | |
// color = mix(color,vec3(0.0,1.0,0.0),plot(st,pct.g)); | |
// color = mix(color,yellow,plot(st,pct.b)); | |
gl_FragColor = vec4(color,1.0); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
#define PI 3.14159265359 | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
vec3 orange = vec3(236.0,62.0,29.0)/255.0; | |
vec3 blue = vec3(79.0,102.0,177.0)/255.0; | |
vec3 yellow = vec3(248.0,227.0,111.0)/255.0; | |
vec3 green = vec3(0.,1.0,0.0); | |
float plot (vec2 st, float pct){ | |
return smoothstep( pct-0.01, pct, st.y) - | |
smoothstep( pct, pct+0.01, st.y); | |
} | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
vec3 color = vec3(0.0); | |
vec3 pct = vec3(st.x)*cos(u_time/4.0); | |
color = mix(color, orange, step(.2, st.x)); | |
color = mix(color, blue, step(.4, st.x)); | |
color = mix(color, yellow, step(.6, st.x)); | |
color = mix(color, green, step(.8, st.x)); | |
gl_FragColor = vec4(color,1.0); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
#define TWO_PI 6.28318530718 | |
uniform vec2 u_resolution; | |
uniform float u_time; | |
uniform vec2 u_mouse; | |
vec3 rgb2hsb( in vec3 c ){ | |
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); | |
vec4 p = mix(vec4(c.bg, K.wz), | |
vec4(c.gb, K.xy), | |
step(c.b, c.g)); | |
vec4 q = mix(vec4(p.xyw, c.r), | |
vec4(c.r, p.yzx), | |
step(p.x, c.r)); | |
float d = q.x - min(q.w, q.y); | |
float e = 1.0e-10; | |
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), | |
d / (q.x + e), | |
q.x); | |
} | |
// Function from Iñigo Quiles | |
// https://www.shadertoy.com/view/MsS3Wc | |
vec3 hsb2rgb( in vec3 c ){ | |
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0), | |
6.0)-3.0)-1.0, | |
0.0, | |
1.0 ); | |
rgb = rgb*rgb*(3.0-2.0*rgb); | |
return c.z * mix(vec3(1.0), rgb, c.y); | |
} | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
vec3 color = vec3(0.0); | |
// Use polar coordinates instead of cartesian | |
vec2 toCenter = vec2(0.5)-st; | |
float angle = atan(toCenter.y,toCenter.x); | |
float radius = length(toCenter)*2.0; | |
// Map the angle (-PI to PI) to the Hue (from 0 to 1) | |
// and the Saturation to the radius | |
angle = angle/TWO_PI + .5; | |
angle = pow(angle, u_mouse.x/u_resolution.x*100.0 + .5); | |
angle = mod(angle + u_time/10.0 + u_mouse.y/u_resolution.y*10.0 + .5, 1.0); | |
color = hsb2rgb(vec3(angle,radius,1.0)); | |
gl_FragColor = vec4(color,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
vec3 color = vec3(0.0); | |
// bottom-left | |
vec2 bl = step(vec2(0.1),st); | |
float pct = bl.x * bl.y; | |
// top-right | |
vec2 tr = smoothstep(vec2(0.1),vec2(.3), 1.0-st); | |
pct *= tr.x * tr.y; | |
vec2 bbl = step(vec2(0.95), 1.0-st); | |
pct = max(pct, bbl.x); | |
pct = max(pct, bbl.y); | |
vec2 ttr = step(vec2(0.95), st); | |
pct = max(pct, ttr.x); | |
pct = max(pct, ttr.y); | |
color = vec3(pct); | |
gl_FragColor = vec4(color,1.0); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float pct = 0.0; | |
float pct2 = 0.0; | |
// a. The DISTANCE from the pixel to the center | |
pct = distance(st,vec2(0.5)); | |
// b. The LENGTH of the vector | |
// from the pixel to the center | |
vec2 toCenter = vec2(0.5)-st; | |
pct = length(toCenter); | |
// c. The SQUARE ROOT of the vector | |
// from the pixel to the center | |
vec2 tC = vec2(6.5)-st*10.0; | |
pct = sqrt(tC.x*tC.x+tC.y*tC.y); | |
vec2 tC2 = vec2(6.5)-st*10.5; | |
pct2 = sqrt(tC2.x*tC2.x+tC2.y*tC2.y); | |
vec3 color = vec3(floor(pct), .3, .4)*vec3(sin(pct2*u_time+ u_time*3.0)); | |
gl_FragColor = vec4( color, 1.0 ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifdef GL_ES | |
precision mediump float; | |
#endif | |
uniform vec2 u_resolution; | |
uniform vec2 u_mouse; | |
uniform float u_time; | |
void main(){ | |
vec2 st = gl_FragCoord.xy/u_resolution; | |
float pct = 0.0; | |
// a. The DISTANCE from the pixel to the center | |
pct = distance(st, vec2(0.5)); | |
vec3 color = vec3(step(0.02, pct)); | |
color = color*vec3(1.0, 1.0, pow(pct, .2)); | |
// vec3 color = vec3(floor(pct + .1), .3, .4); | |
gl_FragColor = vec4( color, 1.0 ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
!function() { | |
var d3 = { | |
version: "3.5.5" | |
}; | |
var d3_arraySlice = [].slice, d3_array = function(list) { | |
return d3_arraySlice.call(list); | |
}; | |
var d3_document = this.document; | |
function d3_documentElement(node) { | |
return node && (node.ownerDocument || node.document || node).documentElement; | |
} | |
function d3_window(node) { | |
return node && (node.ownerDocument && node.ownerDocument.defaultView || node.document && node || node.defaultView); | |
} | |
if (d3_document) { | |
try { | |
d3_array(d3_document.documentElement.childNodes)[0].nodeType; | |
} catch (e) { | |
d3_array = function(list) { | |
var i = list.length, array = new Array(i); | |
while (i--) array[i] = list[i]; | |
return array; | |
}; | |
} | |
} | |
if (!Date.now) Date.now = function() { | |
return +new Date(); | |
}; | |
if (d3_document) { | |
try { | |
d3_document.createElement("DIV").style.setProperty("opacity", 0, ""); | |
} catch (error) { | |
var d3_element_prototype = this.Element.prototype, d3_element_setAttribute = d3_element_prototype.setAttribute, d3_element_setAttributeNS = d3_element_prototype.setAttributeNS, d3_style_prototype = this.CSSStyleDeclaration.prototype, d3_style_setProperty = d3_style_prototype.setProperty; | |
d3_element_prototype.setAttribute = function(name, value) { | |
d3_element_setAttribute.call(this, name, value + ""); | |
}; | |
d3_element_prototype.setAttributeNS = function(space, local, value) { | |
d3_element_setAttributeNS.call(this, space, local, value + ""); | |
}; | |
d3_style_prototype.setProperty = function(name, value, priority) { | |
d3_style_setProperty.call(this, name, value + "", priority); | |
}; | |
} | |
} | |
d3.ascending = d3_ascending; | |
function d3_ascending(a, b) { | |
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN; | |
} | |
d3.descending = function(a, b) { | |
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN; | |
}; | |
d3.min = function(array, f) { | |
var i = -1, n = array.length, a, b; | |
if (arguments.length === 1) { | |
while (++i < n) if ((b = array[i]) != null && b >= b) { | |
a = b; | |
break; | |
} | |
while (++i < n) if ((b = array[i]) != null && a > b) a = b; | |
} else { | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { | |
a = b; | |
break; | |
} | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null && a > b) a = b; | |
} | |
return a; | |
}; | |
d3.max = function(array, f) { | |
var i = -1, n = array.length, a, b; | |
if (arguments.length === 1) { | |
while (++i < n) if ((b = array[i]) != null && b >= b) { | |
a = b; | |
break; | |
} | |
while (++i < n) if ((b = array[i]) != null && b > a) a = b; | |
} else { | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { | |
a = b; | |
break; | |
} | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b > a) a = b; | |
} | |
return a; | |
}; | |
d3.extent = function(array, f) { | |
var i = -1, n = array.length, a, b, c; | |
if (arguments.length === 1) { | |
while (++i < n) if ((b = array[i]) != null && b >= b) { | |
a = c = b; | |
break; | |
} | |
while (++i < n) if ((b = array[i]) != null) { | |
if (a > b) a = b; | |
if (c < b) c = b; | |
} | |
} else { | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null && b >= b) { | |
a = c = b; | |
break; | |
} | |
while (++i < n) if ((b = f.call(array, array[i], i)) != null) { | |
if (a > b) a = b; | |
if (c < b) c = b; | |
} | |
} | |
return [ a, c ]; | |
}; | |
function d3_number(x) { | |
return x === null ? NaN : +x; | |
} | |
function d3_numeric(x) { | |
return !isNaN(x); | |
} | |
d3.sum = function(array, f) { | |
var s = 0, n = array.length, a, i = -1; | |
if (arguments.length === 1) { | |
while (++i < n) if (d3_numeric(a = +array[i])) s += a; | |
} else { | |
while (++i < n) if (d3_numeric(a = +f.call(array, array[i], i))) s += a; | |
} | |
return s; | |
}; | |
d3.mean = function(array, f) { | |
var s = 0, n = array.length, a, i = -1, j = n; | |
if (arguments.length === 1) { | |
while (++i < n) if (d3_numeric(a = d3_number(array[i]))) s += a; else --j; | |
} else { | |
while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) s += a; else --j; | |
} | |
if (j) return s / j; | |
}; | |
d3.quantile = function(values, p) { | |
var H = (values.length - 1) * p + 1, h = Math.floor(H), v = +values[h - 1], e = H - h; | |
return e ? v + e * (values[h] - v) : v; | |
}; | |
d3.median = function(array, f) { | |
var numbers = [], n = array.length, a, i = -1; | |
if (arguments.length === 1) { | |
while (++i < n) if (d3_numeric(a = d3_number(array[i]))) numbers.push(a); | |
} else { | |
while (++i < n) if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) numbers.push(a); | |
} | |
if (numbers.length) return d3.quantile(numbers.sort(d3_ascending), .5); | |
}; | |
d3.variance = function(array, f) { | |
var n = array.length, m = 0, a, d, s = 0, i = -1, j = 0; | |
if (arguments.length === 1) { | |
while (++i < n) { | |
if (d3_numeric(a = d3_number(array[i]))) { | |
d = a - m; | |
m += d / ++j; | |
s += d * (a - m); | |
} | |
} | |
} else { | |
while (++i < n) { | |
if (d3_numeric(a = d3_number(f.call(array, array[i], i)))) { | |
d = a - m; | |
m += d / ++j; | |
s += d * (a - m); | |
} | |
} | |
} | |
if (j > 1) return s / (j - 1); | |
}; | |
d3.deviation = function() { | |
var v = d3.variance.apply(this, arguments); | |
return v ? Math.sqrt(v) : v; | |
}; | |
function d3_bisector(compare) { | |
return { | |
left: function(a, x, lo, hi) { | |
if (arguments.length < 3) lo = 0; | |
if (arguments.length < 4) hi = a.length; | |
while (lo < hi) { | |
var mid = lo + hi >>> 1; | |
if (compare(a[mid], x) < 0) lo = mid + 1; else hi = mid; | |
} | |
return lo; | |
}, | |
right: function(a, x, lo, hi) { | |
if (arguments.length < 3) lo = 0; | |
if (arguments.length < 4) hi = a.length; | |
while (lo < hi) { | |
var mid = lo + hi >>> 1; | |
if (compare(a[mid], x) > 0) hi = mid; else lo = mid + 1; | |
} | |
return lo; | |
} | |
}; | |
} | |
var d3_bisect = d3_bisector(d3_ascending); | |
d3.bisectLeft = d3_bisect.left; | |
d3.bisect = d3.bisectRight = d3_bisect.right; | |
d3.bisector = function(f) { | |
return d3_bisector(f.length === 1 ? function(d, x) { | |
return d3_ascending(f(d), x); | |
} : f); | |
}; | |
d3.shuffle = function(array, i0, i1) { | |
if ((m = arguments.length) < 3) { | |
i1 = array.length; | |
if (m < 2) i0 = 0; | |
} | |
var m = i1 - i0, t, i; | |
while (m) { | |
i = Math.random() * m-- | 0; | |
t = array[m + i0], array[m + i0] = array[i + i0], array[i + i0] = t; | |
} | |
return array; | |
}; | |
d3.permute = function(array, indexes) { | |
var i = indexes.length, permutes = new Array(i); | |
while (i--) permutes[i] = array[indexes[i]]; | |
return permutes; | |
}; | |
d3.pairs = function(array) { | |
var i = 0, n = array.length - 1, p0, p1 = array[0], pairs = new Array(n < 0 ? 0 : n); | |
while (i < n) pairs[i] = [ p0 = p1, p1 = array[++i] ]; | |
return pairs; | |
}; | |
d3.zip = function() { | |
if (!(n = arguments.length)) return []; | |
for (var i = -1, m = d3.min(arguments, d3_zipLength), zips = new Array(m); ++i < m; ) { | |
for (var j = -1, n, zip = zips[i] = new Array(n); ++j < n; ) { | |
zip[j] = arguments[j][i]; | |
} | |
} | |
return zips; | |
}; | |
function d3_zipLength(d) { | |
return d.length; | |
} | |
d3.transpose = function(matrix) { | |
return d3.zip.apply(d3, matrix); | |
}; | |
d3.keys = function(map) { | |
var keys = []; | |
for (var key in map) keys.push(key); | |
return keys; | |
}; | |
d3.values = function(map) { | |
var values = []; | |
for (var key in map) values.push(map[key]); | |
return values; | |
}; | |
d3.entries = function(map) { | |
var entries = []; | |
for (var key in map) entries.push({ | |
key: key, | |
value: map[key] | |
}); | |
return entries; | |
}; | |
d3.merge = function(arrays) { | |
var n = arrays.length, m, i = -1, j = 0, merged, array; | |
while (++i < n) j += arrays[i].length; | |
merged = new Array(j); | |
while (--n >= 0) { | |
array = arrays[n]; | |
m = array.length; | |
while (--m >= 0) { | |
merged[--j] = array[m]; | |
} | |
} | |
return merged; | |
}; | |
var abs = Math.abs; | |
d3.range = function(start, stop, step) { | |
if (arguments.length < 3) { | |
step = 1; | |
if (arguments.length < 2) { | |
stop = start; | |
start = 0; | |
} | |
} | |
if ((stop - start) / step === Infinity) throw new Error("infinite range"); | |
var range = [], k = d3_range_integerScale(abs(step)), i = -1, j; | |
start *= k, stop *= k, step *= k; | |
if (step < 0) while ((j = start + step * ++i) > stop) range.push(j / k); else while ((j = start + step * ++i) < stop) range.push(j / k); | |
return range; | |
}; | |
function d3_range_integerScale(x) { | |
var k = 1; | |
while (x * k % 1) k *= 10; | |
return k; | |
} | |
function d3_class(ctor, properties) { | |
for (var key in properties) { | |
Object.defineProperty(ctor.prototype, key, { | |
value: properties[key], | |
enumerable: false | |
}); | |
} | |
} | |
d3.map = function(object, f) { | |
var map = new d3_Map(); | |
if (object instanceof d3_Map) { | |
object.forEach(function(key, value) { | |
map.set(key, value); | |
}); | |
} else if (Array.isArray(object)) { | |
var i = -1, n = object.length, o; | |
if (arguments.length === 1) while (++i < n) map.set(i, object[i]); else while (++i < n) map.set(f.call(object, o = object[i], i), o); | |
} else { | |
for (var key in object) map.set(key, object[key]); | |
} | |
return map; | |
}; | |
function d3_Map() { | |
this._ = Object.create(null); | |
} | |
var d3_map_proto = "__proto__", d3_map_zero = "\x00"; | |
d3_class(d3_Map, { | |
has: d3_map_has, | |
get: function(key) { | |
return this._[d3_map_escape(key)]; | |
}, | |
set: function(key, value) { | |
return this._[d3_map_escape(key)] = value; | |
}, | |
remove: d3_map_remove, | |
keys: d3_map_keys, | |
values: function() { | |
var values = []; | |
for (var key in this._) values.push(this._[key]); | |
return values; | |
}, | |
entries: function() { | |
var entries = []; | |
for (var key in this._) entries.push({ | |
key: d3_map_unescape(key), | |
value: this._[key] | |
}); | |
return entries; | |
}, | |
size: d3_map_size, | |
empty: d3_map_empty, | |
forEach: function(f) { | |
for (var key in this._) f.call(this, d3_map_unescape(key), this._[key]); | |
} | |
}); | |
function d3_map_escape(key) { | |
return (key += "") === d3_map_proto || key[0] === d3_map_zero ? d3_map_zero + key : key; | |
} | |
function d3_map_unescape(key) { | |
return (key += "")[0] === d3_map_zero ? key.slice(1) : key; | |
} | |
function d3_map_has(key) { | |
return d3_map_escape(key) in this._; | |
} | |
function d3_map_remove(key) { | |
return (key = d3_map_escape(key)) in this._ && delete this._[key]; | |
} | |
function d3_map_keys() { | |
var keys = []; | |
for (var key in this._) keys.push(d3_map_unescape(key)); | |
return keys; | |
} | |
function d3_map_size() { | |
var size = 0; | |
for (var key in this._) ++size; | |
return size; | |
} | |
function d3_map_empty() { | |
for (var key in this._) return false; | |
return true; | |
} | |
d3.nest = function() { | |
var nest = {}, keys = [], sortKeys = [], sortValues, rollup; | |
function map(mapType, array, depth) { | |
if (depth >= keys.length) return rollup ? rollup.call(nest, array) : sortValues ? array.sort(sortValues) : array; | |
var i = -1, n = array.length, key = keys[depth++], keyValue, object, setter, valuesByKey = new d3_Map(), values; | |
while (++i < n) { | |
if (values = valuesByKey.get(keyValue = key(object = array[i]))) { | |
values.push(object); | |
} else { | |
valuesByKey.set(keyValue, [ object ]); | |
} | |
} | |
if (mapType) { | |
object = mapType(); | |
setter = function(keyValue, values) { | |
object.set(keyValue, map(mapType, values, depth)); | |
}; | |
} else { | |
object = {}; | |
setter = function(keyValue, values) { | |
object[keyValue] = map(mapType, values, depth); | |
}; | |
} | |
valuesByKey.forEach(setter); | |
return object; | |
} | |
function entries(map, depth) { | |
if (depth >= keys.length) return map; | |
var array = [], sortKey = sortKeys[depth++]; | |
map.forEach(function(key, keyMap) { | |
array.push({ | |
key: key, | |
values: entries(keyMap, depth) | |
}); | |
}); | |
return sortKey ? array.sort(function(a, b) { | |
return sortKey(a.key, b.key); | |
}) : array; | |
} | |
nest.map = function(array, mapType) { | |
return map(mapType, array, 0); | |
}; | |
nest.entries = function(array) { | |
return entries(map(d3.map, array, 0), 0); | |
}; | |
nest.key = function(d) { | |
keys.push(d); | |
return nest; | |
}; | |
nest.sortKeys = function(order) { | |
sortKeys[keys.length - 1] = order; | |
return nest; | |
}; | |
nest.sortValues = function(order) { | |
sortValues = order; | |
return nest; | |
}; | |
nest.rollup = function(f) { | |
rollup = f; | |
return nest; | |
}; | |
return nest; | |
}; | |
d3.set = function(array) { | |
var set = new d3_Set(); | |
if (array) for (var i = 0, n = array.length; i < n; ++i) set.add(array[i]); | |
return set; | |
}; | |
function d3_Set() { | |
this._ = Object.create(null); | |
} | |
d3_class(d3_Set, { | |
has: d3_map_has, | |
add: function(key) { | |
this._[d3_map_escape(key += "")] = true; | |
return key; | |
}, | |
remove: d3_map_remove, | |
values: d3_map_keys, | |
size: d3_map_size, | |
empty: d3_map_empty, | |
forEach: function(f) { | |
for (var key in this._) f.call(this, d3_map_unescape(key)); | |
} | |
}); | |
d3.behavior = {}; | |
function d3_identity(d) { | |
return d; | |
} | |
d3.rebind = function(target, source) { | |
var i = 1, n = arguments.length, method; | |
while (++i < n) target[method = arguments[i]] = d3_rebind(target, source, source[method]); | |
return target; | |
}; | |
function d3_rebind(target, source, method) { | |
return function() { | |
var value = method.apply(source, arguments); | |
return value === source ? target : value; | |
}; | |
} | |
function d3_vendorSymbol(object, name) { | |
if (name in object) return name; | |
name = name.charAt(0).toUpperCase() + name.slice(1); | |
for (var i = 0, n = d3_vendorPrefixes.length; i < n; ++i) { | |
var prefixName = d3_vendorPrefixes[i] + name; | |
if (prefixName in object) return prefixName; | |
} | |
} | |
var d3_vendorPrefixes = [ "webkit", "ms", "moz", "Moz", "o", "O" ]; | |
function d3_noop() {} | |
d3.dispatch = function() { | |
var dispatch = new d3_dispatch(), i = -1, n = arguments.length; | |
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); | |
return dispatch; | |
}; | |
function d3_dispatch() {} | |
d3_dispatch.prototype.on = function(type, listener) { | |
var i = type.indexOf("."), name = ""; | |
if (i >= 0) { | |
name = type.slice(i + 1); | |
type = type.slice(0, i); | |
} | |
if (type) return arguments.length < 2 ? this[type].on(name) : this[type].on(name, listener); | |
if (arguments.length === 2) { | |
if (listener == null) for (type in this) { | |
if (this.hasOwnProperty(type)) this[type].on(name, null); | |
} | |
return this; | |
} | |
}; | |
function d3_dispatch_event(dispatch) { | |
var listeners = [], listenerByName = new d3_Map(); | |
function event() { | |
var z = listeners, i = -1, n = z.length, l; | |
while (++i < n) if (l = z[i].on) l.apply(this, arguments); | |
return dispatch; | |
} | |
event.on = function(name, listener) { | |
var l = listenerByName.get(name), i; | |
if (arguments.length < 2) return l && l.on; | |
if (l) { | |
l.on = null; | |
listeners = listeners.slice(0, i = listeners.indexOf(l)).concat(listeners.slice(i + 1)); | |
listenerByName.remove(name); | |
} | |
if (listener) listeners.push(listenerByName.set(name, { | |
on: listener | |
})); | |
return dispatch; | |
}; | |
return event; | |
} | |
d3.event = null; | |
function d3_eventPreventDefault() { | |
d3.event.preventDefault(); | |
} | |
function d3_eventSource() { | |
var e = d3.event, s; | |
while (s = e.sourceEvent) e = s; | |
return e; | |
} | |
function d3_eventDispatch(target) { | |
var dispatch = new d3_dispatch(), i = 0, n = arguments.length; | |
while (++i < n) dispatch[arguments[i]] = d3_dispatch_event(dispatch); | |
dispatch.of = function(thiz, argumentz) { | |
return function(e1) { | |
try { | |
var e0 = e1.sourceEvent = d3.event; | |
e1.target = target; | |
d3.event = e1; | |
dispatch[e1.type].apply(thiz, argumentz); | |
} finally { | |
d3.event = e0; | |
} | |
}; | |
}; | |
return dispatch; | |
} | |
d3.requote = function(s) { | |
return s.replace(d3_requote_re, "\\$&"); | |
}; | |
var d3_requote_re = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g; | |
var d3_subclass = {}.__proto__ ? function(object, prototype) { | |
object.__proto__ = prototype; | |
} : function(object, prototype) { | |
for (var property in prototype) object[property] = prototype[property]; | |
}; | |
function d3_selection(groups) { | |
d3_subclass(groups, d3_selectionPrototype); | |
return groups; | |
} | |
var d3_select = function(s, n) { | |
return n.querySelector(s); | |
}, d3_selectAll = function(s, n) { | |
return n.querySelectorAll(s); | |
}, d3_selectMatches = function(n, s) { | |
var d3_selectMatcher = n.matches || n[d3_vendorSymbol(n, "matchesSelector")]; | |
d3_selectMatches = function(n, s) { | |
return d3_selectMatcher.call(n, s); | |
}; | |
return d3_selectMatches(n, s); | |
}; | |
if (typeof Sizzle === "function") { | |
d3_select = function(s, n) { | |
return Sizzle(s, n)[0] || null; | |
}; | |
d3_selectAll = Sizzle; | |
d3_selectMatches = Sizzle.matchesSelector; | |
} | |
d3.selection = function() { | |
return d3.select(d3_document.documentElement); | |
}; | |
var d3_selectionPrototype = d3.selection.prototype = []; | |
d3_selectionPrototype.select = function(selector) { | |
var subgroups = [], subgroup, subnode, group, node; | |
selector = d3_selection_selector(selector); | |
for (var j = -1, m = this.length; ++j < m; ) { | |
subgroups.push(subgroup = []); | |
subgroup.parentNode = (group = this[j]).parentNode; | |
for (var i = -1, n = group.length; ++i < n; ) { | |
if (node = group[i]) { | |
subgroup.push(subnode = selector.call(node, node.__data__, i, j)); | |
if (subnode && "__data__" in node) subnode.__data__ = node.__data__; | |
} else { | |
subgroup.push(null); | |
} | |
} | |
} | |
return d3_selection(subgroups); | |
}; | |
function d3_selection_selector(selector) { | |
return typeof selector === "function" ? selector : function() { | |
return d3_select(selector, this); | |
}; | |
} | |
d3_selectionPrototype.selectAll = function(selector) { | |
var subgroups = [], subgroup, node; | |
selector = d3_selection_selectorAll(selector); | |
for (var j = -1, m = this.length; ++j < m; ) { | |
for (var group = this[j], i = -1, n = group.length; ++i < n; ) { | |
if (node = group[i]) { | |
subgroups.push(subgroup = d3_array(selector.call(node, node.__data__, i, j))); | |
subgroup.parentNode = node; | |
} | |
} | |
} | |
return d3_selection(subgroups); | |
}; | |
function d3_selection_selectorAll(selector) { | |
return typeof selector === "function" ? selector : function() { | |
return d3_selectAll(selector, this); | |
}; | |
} | |
var d3_nsPrefix = { | |
svg: "http://www.w3.org/2000/svg", | |
xhtml: "http://www.w3.org/1999/xhtml", | |
xlink: "http://www.w3.org/1999/xlink", | |
xml: "http://www.w3.org/XML/1998/namespace", | |
xmlns: "http://www.w3.org/2000/xmlns/" | |
}; | |
d3.ns = { | |
prefix: d3_nsPrefix, | |
qualify: function(name) { | |
var i = name.indexOf(":"), prefix = name; | |
if (i >= 0) { | |
prefix = name.slice(0, i); | |
name = name.slice(i + 1); | |
} | |
return d3_nsPrefix.hasOwnProperty(prefix) ? { | |
space: d3_nsPrefix[prefix], | |
local: name | |
} : name; | |
} | |
}; | |
d3_selectionPrototype.attr = function(name, value) { | |
if (arguments.length < 2) { | |
if (typeof name === "string") { | |
var node = this.node(); | |
name = d3.ns.qualify(name); | |
return name.local ? node.getAttributeNS(name.space, name.local) : node.getAttribute(name); | |
} | |
for (value in name) this.each(d3_selection_attr(value, name[value])); | |
return this; | |
} | |
return this.each(d3_selection_attr(name, value)); | |
}; | |
function d3_selection_attr(name, value) { | |
name = d3.ns.qualify(name); | |
function attrNull() { | |
this.removeAttribute(name); | |
} | |
function attrNullNS() { | |
this.removeAttributeNS(name.space, name.local); | |
} | |
function attrConstant() { | |
this.setAttribute(name, value); | |
} | |
function attrConstantNS() { | |
this.setAttributeNS(name.space, name.local, value); | |
} | |
function attrFunction() { | |
var x = value.apply(this, arguments); | |
if (x == null) this.removeAttribute(name); else this.setAttribute(name, x); | |
} | |
function attrFunctionNS() { | |
var x = value.apply(this, arguments); | |
if (x == null) this.removeAttributeNS(name.space, name.local); else this.setAttributeNS(name.space, name.local, x); | |
} | |
return value == null ? name.local ? attrNullNS : attrNull : typeof value === "function" ? name.local ? attrFunctionNS : attrFunction : name.local ? attrConstantNS : attrConstant; | |
} | |
function d3_collapse(s) { | |
return s.trim().replace(/\s+/g, " "); | |
} | |
d3_selectionPrototype.classed = function(name, value) { | |
if (arguments.length < 2) { | |
if (typeof name === "string") { | |
var node = this.node(), n = (name = d3_selection_classes(name)).length, i = -1; | |
if (value = node.classList) { | |
while (++i < n) if (!value.contains(name[i])) return false; | |
} else { | |
value = node.getAttribute("class"); | |
while (++i < n) if (!d3_selection_classedRe(name[i]).test(value)) return false; | |
} | |
return true; | |
} | |
for (value in name) this.each(d3_selection_classed(value, name[value])); | |
return this; | |
} | |
return this.each(d3_selection_classed(name, value)); | |
}; | |
function d3_selection_classedRe(name) { | |
return new RegExp("(?:^|\\s+)" + d3.requote(name) + "(?:\\s+|$)", "g"); | |
} | |
function d3_selection_classes(name) { | |
return (name + "").trim().split(/^|\s+/); | |
} | |
function d3_selection_classed(name, value) { | |
name = d3_selection_classes(name).map(d3_selection_classedName); | |
var n = name.length; | |
function classedConstant() { | |
var i = -1; | |
while (++i < n) name[i](this, value); | |
} | |
function classedFunction() { | |
var i = -1, x = value.apply(this, arguments); | |
while (++i < n) name[i](this, x); | |
} | |
return typeof value === "function" ? classedFunction : classedConstant; | |
} | |
function d3_selection_classedName(name) { | |
var re = d3_selection_classedRe(name); | |
return function(node, value) { | |
if (c = node.classList) return value ? c.add(name) : c.remove(name); | |
var c = node.getAttribute("class") || ""; | |
if (value) { | |
re.lastIndex = 0; | |
if (!re.test(c)) node.setAttribute("class", d3_collapse(c + " " + name)); | |
} else { | |
node.setAttribute("class", d3_collapse(c.replace(re, " "))); | |
} | |
}; | |
} | |
d3_selectionPrototype.style = function(name, value, priority) { | |
var n = arguments.length; | |
if (n < 3) { | |
if (typeof name !== "string") { | |
if (n < 2) value = ""; | |
for (priority in name) this.each(d3_selection_style(priority, name[priority], value)); | |
return this; | |
} | |
if (n < 2) { | |
var node = this.node(); | |
return d3_window(node).getComputedStyle(node, null).getPropertyValue(name); | |
} | |
priority = ""; | |
} | |
return this.each(d3_selection_style(name, value, priority)); | |
}; | |
function d3_selection_style(name, value, priority) { | |
function styleNull() { | |
this.style.removeProperty(name); | |
} | |
function styleConstant() { | |
this.style.setProperty(name, value, priority); | |
} | |
function styleFunction() { | |
var x = value.apply(this, arguments); | |
if (x == null) this.style.removeProperty(name); else this.style.setProperty(name, x, priority); | |
} | |
return value == null ? styleNull : typeof value === "function" ? styleFunction : styleConstant; | |
} | |
d3_selectionPrototype.property = function(name, value) { | |
if (arguments.length < 2) { | |
if (typeof name === "string") return this.node()[name]; | |
for (value in name) this.each(d3_selection_property(value, name[value])); | |
return this; | |
} | |
return this.each(d3_selection_property(name, value)); | |
}; | |
function d3_selection_property(name, value) { | |
function propertyNull() { | |
delete this[name]; | |
} | |
function propertyConstant() { | |
this[name] = value; | |
} | |
function propertyFunction() { | |
var x = value.apply(this, arguments); | |
if (x == null) delete this[name]; else this[name] = x; | |
} | |
return value == null ? propertyNull : typeof value === "function" ? propertyFunction : propertyConstant; | |
} | |
d3_selectionPrototype.text = function(value) { | |
return arguments.length ? this.each(typeof value === "function" ? function() { | |
var v = value.apply(this, arguments); | |
this.textContent = v == null ? "" : v; | |
} : value == null ? function() { | |
this.textContent = ""; | |
} : function() { | |
this.textContent = value; | |
}) : this.node().textContent; | |
}; | |
d3_selectionPrototype.html = function(value) { | |
return arguments.length ? this.each(typeof value === "function" ? function() { | |
var v = value.apply(this, arguments); | |
this.innerHTML = v == null ? "" : v; | |
} : value == null ? function() { | |
this.innerHTML = ""; | |
} : function() { | |
this.innerHTML = value; | |
}) : this.node().innerHTML; | |
}; | |
d3_selectionPrototype.append = function(name) { | |
name = d3_selection_creator(name); | |
return this.select(function() { | |
return this.appendChild(name.apply(this, arguments)); | |
}); | |
}; | |
function d3_selection_creator(name) { | |
function create() { | |
var document = this.ownerDocument, namespace = this.namespaceURI; | |
return namespace ? document.createElementNS(namespace, name) : document.createElement(name); | |
} | |
function createNS() { | |
return this.ownerDocument.createElementNS(name.space, name.local); | |
} | |
return typeof name === "function" ? name : (name = d3.ns.qualify(name)).local ? createNS : create; | |
} | |
d3_selectionPrototype.insert = function(name, before) { | |
name = d3_selection_creator(name); | |
before = d3_selection_selector(before); | |
return this.select(function() { | |
return this.insertBefore(name.apply(this, arguments), before.apply(this, arguments) || null); | |
}); | |
}; | |
d3_selectionPrototype.remove = function() { | |
return this.each(d3_selectionRemove); | |
}; | |
function d3_selectionRemove() { | |
var parent = this.parentNode; | |
if (parent) parent.removeChild(this); | |
} | |
d3_selectionPrototype.data = function(value, key) { | |
var i = -1, n = this.length, group, node; | |
if (!arguments.length) { | |
value = new Array(n = (group = this[0]).length); | |
while (++i < n) { | |
if (node = group[i]) { | |
value[i] = node.__data__; | |
} | |
} | |
return value; | |
} | |
function bind(group, groupData) { | |
var i, n = group.length, m = groupData.length, n0 = Math.min(n, m), updateNodes = new Array(m), enterNodes = new Array(m), exitNodes = new Array(n), node, nodeData; | |
if (key) { | |
var nodeByKeyValue = new d3_Map(), keyValues = new Array(n), keyValue; | |
for (i = -1; ++i < n; ) { | |
if (nodeByKeyValue.has(keyValue = key.call(node = group[i], node.__data__, i))) { | |
exitNodes[i] = node; | |
} else { | |
nodeByKeyValue.set(keyValue, node); | |
} | |
keyValues[i] = keyValue; | |
} | |
for (i = -1; ++i < m; ) { | |
if (!(node = nodeByKeyValue.get(keyValue = key.call(groupData, nodeData = groupData[i], i)))) { | |
enterNodes[i] = d3_selection_dataNode(nodeData); | |
} else if (node !== true) { | |
updateNodes[i] = node; | |
node.__data__ = nodeData; | |
} | |
nodeByKeyValue.set(keyValue, true); | |
} | |
for (i = -1; ++i < n; ) { | |
if (nodeByKeyValue.get(keyValues[i]) !== true) { | |
exitNodes[i] = group[i]; | |
} | |
} | |
} else { | |
for (i = -1; ++i < n0; ) { | |
node = group[i]; | |
nodeData = groupData[i]; | |
if (node) { | |
node.__data__ = nodeData; | |
updateNodes[i] = node; | |
} else { | |
enterNodes[i] = d3_selection_dataNode(nodeData); | |
} | |
} | |
for (;i < m; ++i) { | |
enterNodes[i] = d3_selection_dataNode(groupData[i]); | |
} | |
for (;i < n; ++i) { | |
exitNodes[i] = group[i]; | |
} | |
} | |
enterNodes.update = updateNodes; | |
enterNodes.parentNode = updateNodes.parentNode = exitNodes.parentNode = group.parentNode; | |
enter.push(enterNodes); | |
update.push(updateNodes); | |
exit.push(exitNodes); | |
} | |
var enter = d3_selection_enter([]), update = d3_selection([]), exit = d3_selection([]); | |
if (typeof value === "function") { | |
while (++i < n) { | |
bind(group = this[i], value.call(group, group.parentNode.__data__, i)); | |
} | |
} else { | |
while (++i < n) { | |
bind(group = this[i], value); | |
} | |
} | |
update.enter = function() { | |
return enter; | |
}; | |
update.exit = function() { | |
return exit; | |
}; | |
return update; | |
}; | |
function d3_selection_dataNode(data) { | |
return { | |
__data__: data | |
}; | |
} | |
d3_selectionPrototype.datum = function(value) { | |
return arguments.length ? this.property("__data__", value) : this.property("__data__"); | |
}; | |
d3_selectionPrototype.filter = function(filter) { | |
var subgroups = [], subgroup, group, node; | |
if (typeof filter !== "function") filter = d3_selection_filter(filter); | |
for (var j = 0, m = this.length; j < m; j++) { | |
subgroups.push(subgroup = []); | |
subgroup.parentNode = (group = this[j]).parentNode; | |
for (var i = 0, n = group.length; i < n; i++) { | |
if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { | |
subgroup.push(node); | |
} | |
} | |
} | |
return d3_selection(subgroups); | |
}; | |
function d3_selection_filter(selector) { | |
return function() { | |
return d3_selectMatches(this, selector); | |
}; | |
} | |
d3_selectionPrototype.order = function() { | |
for (var j = -1, m = this.length; ++j < m; ) { | |
for (var group = this[j], i = group.length - 1, next = group[i], node; --i >= 0; ) { | |
if (node = group[i]) { | |
if (next && next !== node.nextSibling) next.parentNode.insertBefore(node, next); | |
next = node; | |
} | |
} | |
} | |
return this; | |
}; | |
d3_selectionPrototype.sort = function(comparator) { | |
comparator = d3_selection_sortComparator.apply(this, arguments); | |
for (var j = -1, m = this.length; ++j < m; ) this[j].sort(comparator); | |
return this.order(); | |
}; | |
function d3_selection_sortComparator(comparator) { | |
if (!arguments.length) comparator = d3_ascending; | |
return function(a, b) { | |
return a && b ? comparator(a.__data__, b.__data__) : !a - !b; | |
}; | |
} | |
d3_selectionPrototype.each = function(callback) { | |
return d3_selection_each(this, function(node, i, j) { | |
callback.call(node, node.__data__, i, j); | |
}); | |
}; | |
function d3_selection_each(groups, callback) { | |
for (var j = 0, m = groups.length; j < m; j++) { | |
for (var group = groups[j], i = 0, n = group.length, node; i < n; i++) { | |
if (node = group[i]) callback(node, i, j); | |
} | |
} | |
return groups; | |
} | |
d3_selectionPrototype.call = function(callback) { | |
var args = d3_array(arguments); | |
callback.apply(args[0] = this, args); | |
return this; | |
}; | |
d3_selectionPrototype.empty = function() { | |
return !this.node(); | |
}; | |
d3_selectionPrototype.node = function() { | |
for (var j = 0, m = this.length; j < m; j++) { | |
for (var group = this[j], i = 0, n = group.length; i < n; i++) { | |
var node = group[i]; | |
if (node) return node; | |
} | |
} | |
return null; | |
}; | |
d3_selectionPrototype.size = function() { | |
var n = 0; | |
d3_selection_each(this, function() { | |
++n; | |
}); | |
return n; | |
}; | |
function d3_selection_enter(selection) { | |
d3_subclass(selection, d3_selection_enterPrototype); | |
return selection; | |
} | |
var d3_selection_enterPrototype = []; | |
d3.selection.enter = d3_selection_enter; | |
d3.selection.enter.prototype = d3_selection_enterPrototype; | |
d3_selection_enterPrototype.append = d3_selectionPrototype.append; | |
d3_selection_enterPrototype.empty = d3_selectionPrototype.empty; | |
d3_selection_enterPrototype.node = d3_selectionPrototype.node; | |
d3_selection_enterPrototype.call = d3_selectionPrototype.call; | |
d3_selection_enterPrototype.size = d3_selectionPrototype.size; | |
d3_selection_enterPrototype.select = function(selector) { | |
var subgroups = [], subgroup, subnode, upgroup, group, node; | |
for (var j = -1, m = this.length; ++j < m; ) { | |
upgroup = (group = this[j]).update; | |
subgroups.push(subgroup = []); | |
subgroup.parentNode = group.parentNode; | |
for (var i = -1, n = group.length; ++i < n; ) { | |
if (node = group[i]) { | |
subgroup.push(upgroup[i] = subnode = selector.call(group.parentNode, node.__data__, i, j)); | |
subnode.__data__ = node.__data__; | |
} else { | |
subgroup.push(null); | |
} | |
} | |
} | |
return d3_selection(subgroups); | |
}; | |
d3_selection_enterPrototype.insert = function(name, before) { | |
if (arguments.length < 2) before = d3_selection_enterInsertBefore(this); | |
return d3_selectionPrototype.insert.call(this, name, before); | |
}; | |
function d3_selection_enterInsertBefore(enter) { | |
var i0, j0; | |
return function(d, i, j) { | |
var group = enter[j].update, n = group.length, node; | |
if (j != j0) j0 = j, i0 = 0; | |
if (i >= i0) i0 = i + 1; | |
while (!(node = group[i0]) && ++i0 < n) ; | |
return node; | |
}; | |
} | |
d3.select = function(node) { | |
var group; | |
if (typeof node === "string") { | |
group = [ d3_select(node, d3_document) ]; | |
group.parentNode = d3_document.documentElement; | |
} else { | |
group = [ node ]; | |
group.parentNode = d3_documentElement(node); | |
} | |
return d3_selection([ group ]); | |
}; | |
d3.selectAll = function(nodes) { | |
var group; | |
if (typeof nodes === "string") { | |
group = d3_array(d3_selectAll(nodes, d3_document)); | |
group.parentNode = d3_document.documentElement; | |
} else { | |
group = nodes; | |
group.parentNode = null; | |
} | |
return d3_selection([ group ]); | |
}; | |
d3_selectionPrototype.on = function(type, listener, capture) { | |
var n = arguments.length; | |
if (n < 3) { | |
if (typeof type !== "string") { | |
if (n < 2) listener = false; | |
for (capture in type) this.each(d3_selection_on(capture, type[capture], listener)); | |
return this; | |
} | |
if (n < 2) return (n = this.node()["__on" + type]) && n._; | |
capture = false; | |
} | |
return this.each(d3_selection_on(type, listener, capture)); | |
}; | |
function d3_selection_on(type, listener, capture) { | |
var name = "__on" + type, i = type.indexOf("."), wrap = d3_selection_onListener; | |
if (i > 0) type = type.slice(0, i); | |
var filter = d3_selection_onFilters.get(type); | |
if (filter) type = filter, wrap = d3_selection_onFilter; | |
function onRemove() { | |
var l = this[name]; | |
if (l) { | |
this.removeEventListener(type, l, l.$); | |
delete this[name]; | |
} | |
} | |
function onAdd() { | |
var l = wrap(listener, d3_array(arguments)); | |
onRemove.call(this); | |
this.addEventListener(type, this[name] = l, l.$ = capture); | |
l._ = listener; | |
} | |
function removeAll() { | |
var re = new RegExp("^__on([^.]+)" + d3.requote(type) + "$"), match; | |
for (var name in this) { | |
if (match = name.match(re)) { | |
var l = this[name]; | |
this.removeEventListener(match[1], l, l.$); | |
delete this[name]; | |
} | |
} | |
} | |
return i ? listener ? onAdd : onRemove : listener ? d3_noop : removeAll; | |
} | |
var d3_selection_onFilters = d3.map({ | |
mouseenter: "mouseover", | |
mouseleave: "mouseout" | |
}); | |
if (d3_document) { | |
d3_selection_onFilters.forEach(function(k) { | |
if ("on" + k in d3_document) d3_selection_onFilters.remove(k); | |
}); | |
} | |
function d3_selection_onListener(listener, argumentz) { | |
return function(e) { | |
var o = d3.event; | |
d3.event = e; | |
argumentz[0] = this.__data__; | |
try { | |
listener.apply(this, argumentz); | |
} finally { | |
d3.event = o; | |
} | |
}; | |
} | |
function d3_selection_onFilter(listener, argumentz) { | |
var l = d3_selection_onListener(listener, argumentz); | |
return function(e) { | |
var target = this, related = e.relatedTarget; | |
if (!related || related !== target && !(related.compareDocumentPosition(target) & 8)) { | |
l.call(target, e); | |
} | |
}; | |
} | |
var d3_event_dragSelect, d3_event_dragId = 0; | |
function d3_event_dragSuppress(node) { | |
var name = ".dragsuppress-" + ++d3_event_dragId, click = "click" + name, w = d3.select(d3_window(node)).on("touchmove" + name, d3_eventPreventDefault).on("dragstart" + name, d3_eventPreventDefault).on("selectstart" + name, d3_eventPreventDefault); | |
if (d3_event_dragSelect == null) { | |
d3_event_dragSelect = "onselectstart" in node ? false : d3_vendorSymbol(node.style, "userSelect"); | |
} | |
if (d3_event_dragSelect) { | |
var style = d3_documentElement(node).style, select = style[d3_event_dragSelect]; | |
style[d3_event_dragSelect] = "none"; | |
} | |
return function(suppressClick) { | |
w.on(name, null); | |
if (d3_event_dragSelect) style[d3_event_dragSelect] = select; | |
if (suppressClick) { | |
var off = function() { | |
w.on(click, null); | |
}; | |
w.on(click, function() { | |
d3_eventPreventDefault(); | |
off(); | |
}, true); | |
setTimeout(off, 0); | |
} | |
}; | |
} | |
d3.mouse = function(container) { | |
return d3_mousePoint(container, d3_eventSource()); | |
}; | |
var d3_mouse_bug44083 = this.navigator && /WebKit/.test(this.navigator.userAgent) ? -1 : 0; | |
function d3_mousePoint(container, e) { | |
if (e.changedTouches) e = e.changedTouches[0]; | |
var svg = container.ownerSVGElement || container; | |
if (svg.createSVGPoint) { | |
var point = svg.createSVGPoint(); | |
if (d3_mouse_bug44083 < 0) { | |
var window = d3_window(container); | |
if (window.scrollX || window.scrollY) { | |
svg = d3.select("body").append("svg").style({ | |
position: "absolute", | |
top: 0, | |
left: 0, | |
margin: 0, | |
padding: 0, | |
border: "none" | |
}, "important"); | |
var ctm = svg[0][0].getScreenCTM(); | |
d3_mouse_bug44083 = !(ctm.f || ctm.e); | |
svg.remove(); | |
} | |
} | |
if (d3_mouse_bug44083) point.x = e.pageX, point.y = e.pageY; else point.x = e.clientX, | |
point.y = e.clientY; | |
point = point.matrixTransform(container.getScreenCTM().inverse()); | |
return [ point.x, point.y ]; | |
} | |
var rect = container.getBoundingClientRect(); | |
return [ e.clientX - rect.left - container.clientLeft, e.clientY - rect.top - container.clientTop ]; | |
} | |
d3.touch = function(container, touches, identifier) { | |
if (arguments.length < 3) identifier = touches, touches = d3_eventSource().changedTouches; | |
if (touches) for (var i = 0, n = touches.length, touch; i < n; ++i) { | |
if ((touch = touches[i]).identifier === identifier) { | |
return d3_mousePoint(container, touch); | |
} | |
} | |
}; | |
d3.behavior.drag = function() { | |
var event = d3_eventDispatch(drag, "drag", "dragstart", "dragend"), origin = null, mousedown = dragstart(d3_noop, d3.mouse, d3_window, "mousemove", "mouseup"), touchstart = dragstart(d3_behavior_dragTouchId, d3.touch, d3_identity, "touchmove", "touchend"); | |
function drag() { | |
this.on("mousedown.drag", mousedown).on("touchstart.drag", touchstart); | |
} | |
function dragstart(id, position, subject, move, end) { | |
return function() { | |
var that = this, target = d3.event.target, parent = that.parentNode, dispatch = event.of(that, arguments), dragged = 0, dragId = id(), dragName = ".drag" + (dragId == null ? "" : "-" + dragId), dragOffset, dragSubject = d3.select(subject(target)).on(move + dragName, moved).on(end + dragName, ended), dragRestore = d3_event_dragSuppress(target), position0 = position(parent, dragId); | |
if (origin) { | |
dragOffset = origin.apply(that, arguments); | |
dragOffset = [ dragOffset.x - position0[0], dragOffset.y - position0[1] ]; | |
} else { | |
dragOffset = [ 0, 0 ]; | |
} | |
dispatch({ | |
type: "dragstart" | |
}); | |
function moved() { | |
var position1 = position(parent, dragId), dx, dy; | |
if (!position1) return; | |
dx = position1[0] - position0[0]; | |
dy = position1[1] - position0[1]; | |
dragged |= dx | dy; | |
position0 = position1; | |
dispatch({ | |
type: "drag", | |
x: position1[0] + dragOffset[0], | |
y: position1[1] + dragOffset[1], | |
dx: dx, | |
dy: dy | |
}); | |
} | |
function ended() { | |
if (!position(parent, dragId)) return; | |
dragSubject.on(move + dragName, null).on(end + dragName, null); | |
dragRestore(dragged && d3.event.target === target); | |
dispatch({ | |
type: "dragend" | |
}); | |
} | |
}; | |
} | |
drag.origin = function(x) { | |
if (!arguments.length) return origin; | |
origin = x; | |
return drag; | |
}; | |
return d3.rebind(drag, event, "on"); | |
}; | |
function d3_behavior_dragTouchId() { | |
return d3.event.changedTouches[0].identifier; | |
} | |
d3.touches = function(container, touches) { | |
if (arguments.length < 2) touches = d3_eventSource().touches; | |
return touches ? d3_array(touches).map(function(touch) { | |
var point = d3_mousePoint(container, touch); | |
point.identifier = touch.identifier; | |
return point; | |
}) : []; | |
}; | |
var ε = 1e-6, ε2 = ε * ε, π = Math.PI, τ = 2 * π, τε = τ - ε, halfπ = π / 2, d3_radians = π / 180, d3_degrees = 180 / π; | |
function d3_sgn(x) { | |
return x > 0 ? 1 : x < 0 ? -1 : 0; | |
} | |
function d3_cross2d(a, b, c) { | |
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]); | |
} | |
function d3_acos(x) { | |
return x > 1 ? 0 : x < -1 ? π : Math.acos(x); | |
} | |
function d3_asin(x) { | |
return x > 1 ? halfπ : x < -1 ? -halfπ : Math.asin(x); | |
} | |
function d3_sinh(x) { | |
return ((x = Math.exp(x)) - 1 / x) / 2; | |
} | |
function d3_cosh(x) { | |
return ((x = Math.exp(x)) + 1 / x) / 2; | |
} | |
function d3_tanh(x) { | |
return ((x = Math.exp(2 * x)) - 1) / (x + 1); | |
} | |
function d3_haversin(x) { | |
return (x = Math.sin(x / 2)) * x; | |
} | |
var ρ = Math.SQRT2, ρ2 = 2, ρ4 = 4; | |
d3.interpolateZoom = function(p0, p1) { | |
var ux0 = p0[0], uy0 = p0[1], w0 = p0[2], ux1 = p1[0], uy1 = p1[1], w1 = p1[2]; | |
var dx = ux1 - ux0, dy = uy1 - uy0, d2 = dx * dx + dy * dy, d1 = Math.sqrt(d2), b0 = (w1 * w1 - w0 * w0 + ρ4 * d2) / (2 * w0 * ρ2 * d1), b1 = (w1 * w1 - w0 * w0 - ρ4 * d2) / (2 * w1 * ρ2 * d1), r0 = Math.log(Math.sqrt(b0 * b0 + 1) - b0), r1 = Math.log(Math.sqrt(b1 * b1 + 1) - b1), dr = r1 - r0, S = (dr || Math.log(w1 / w0)) / ρ; | |
function interpolate(t) { | |
var s = t * S; | |
if (dr) { | |
var coshr0 = d3_cosh(r0), u = w0 / (ρ2 * d1) * (coshr0 * d3_tanh(ρ * s + r0) - d3_sinh(r0)); | |
return [ ux0 + u * dx, uy0 + u * dy, w0 * coshr0 / d3_cosh(ρ * s + r0) ]; | |
} | |
return [ ux0 + t * dx, uy0 + t * dy, w0 * Math.exp(ρ * s) ]; | |
} | |
interpolate.duration = S * 1e3; | |
return interpolate; | |
}; | |
d3.behavior.zoom = function() { | |
var view = { | |
x: 0, | |
y: 0, | |
k: 1 | |
}, translate0, center0, center, size = [ 960, 500 ], scaleExtent = d3_behavior_zoomInfinity, duration = 250, zooming = 0, mousedown = "mousedown.zoom", mousemove = "mousemove.zoom", mouseup = "mouseup.zoom", mousewheelTimer, touchstart = "touchstart.zoom", touchtime, event = d3_eventDispatch(zoom, "zoomstart", "zoom", "zoomend"), x0, x1, y0, y1; | |
if (!d3_behavior_zoomWheel) { | |
d3_behavior_zoomWheel = "onwheel" in d3_document ? (d3_behavior_zoomDelta = function() { | |
return -d3.event.deltaY * (d3.event.deltaMode ? 120 : 1); | |
}, "wheel") : "onmousewheel" in d3_document ? (d3_behavior_zoomDelta = function() { | |
return d3.event.wheelDelta; | |
}, "mousewheel") : (d3_behavior_zoomDelta = function() { | |
return -d3.event.detail; | |
}, "MozMousePixelScroll"); | |
} | |
function zoom(g) { | |
g.on(mousedown, mousedowned).on(d3_behavior_zoomWheel + ".zoom", mousewheeled).on("dblclick.zoom", dblclicked).on(touchstart, touchstarted); | |
} | |
zoom.event = function(g) { | |
g.each(function() { | |
var dispatch = event.of(this, arguments), view1 = view; | |
if (d3_transitionInheritId) { | |
d3.select(this).transition().each("start.zoom", function() { | |
view = this.__chart__ || { | |
x: 0, | |
y: 0, | |
k: 1 | |
}; | |
zoomstarted(dispatch); | |
}).tween("zoom:zoom", function() { | |
var dx = size[0], dy = size[1], cx = center0 ? center0[0] : dx / 2, cy = center0 ? center0[1] : dy / 2, i = d3.interpolateZoom([ (cx - view.x) / view.k, (cy - view.y) / view.k, dx / view.k ], [ (cx - view1.x) / view1.k, (cy - view1.y) / view1.k, dx / view1.k ]); | |
return function(t) { | |
var l = i(t), k = dx / l[2]; | |
this.__chart__ = view = { | |
x: cx - l[0] * k, | |
y: cy - l[1] * k, | |
k: k | |
}; | |
zoomed(dispatch); | |
}; | |
}).each("interrupt.zoom", function() { | |
zoomended(dispatch); | |
}).each("end.zoom", function() { | |
zoomended(dispatch); | |
}); | |
} else { | |
this.__chart__ = view; | |
zoomstarted(dispatch); | |
zoomed(dispatch); | |
zoomended(dispatch); | |
} | |
}); | |
}; | |
zoom.translate = function(_) { | |
if (!arguments.length) return [ view.x, view.y ]; | |
view = { | |
x: +_[0], | |
y: +_[1], | |
k: view.k | |
}; | |
rescale(); | |
return zoom; | |
}; | |
zoom.scale = function(_) { | |
if (!arguments.length) return view.k; | |
view = { | |
x: view.x, | |
y: view.y, | |
k: +_ | |
}; | |
rescale(); | |
return zoom; | |
}; | |
zoom.scaleExtent = function(_) { | |
if (!arguments.length) return scaleExtent; | |
scaleExtent = _ == null ? d3_behavior_zoomInfinity : [ +_[0], +_[1] ]; | |
return zoom; | |
}; | |
zoom.center = function(_) { | |
if (!arguments.length) return center; | |
center = _ && [ +_[0], +_[1] ]; | |
return zoom; | |
}; | |
zoom.size = function(_) { | |
if (!arguments.length) return size; | |
size = _ && [ +_[0], +_[1] ]; | |
return zoom; | |
}; | |
zoom.duration = function(_) { | |
if (!arguments.length) return duration; | |
duration = +_; | |
return zoom; | |
}; | |
zoom.x = function(z) { | |
if (!arguments.length) return x1; | |
x1 = z; | |
x0 = z.copy(); | |
view = { | |
x: 0, | |
y: 0, | |
k: 1 | |
}; | |
return zoom; | |
}; | |
zoom.y = function(z) { | |
if (!arguments.length) return y1; | |
y1 = z; | |
y0 = z.copy(); | |
view = { | |
x: 0, | |
y: 0, | |
k: 1 | |
}; | |
return zoom; | |
}; | |
function location(p) { | |
return [ (p[0] - view.x) / view.k, (p[1] - view.y) / view.k ]; | |
} | |
function point(l) { | |
return [ l[0] * view.k + view.x, l[1] * view.k + view.y ]; | |
} | |
function scaleTo(s) { | |
view.k = Math.max(scaleExtent[0], Math.min(scaleExtent[1], s)); | |
} | |
function translateTo(p, l) { | |
l = point(l); | |
view.x += p[0] - l[0]; | |
view.y += p[1] - l[1]; | |
} | |
function zoomTo(that, p, l, k) { | |
that.__chart__ = { | |
x: view.x, | |
y: view.y, | |
k: view.k | |
}; | |
scaleTo(Math.pow(2, k)); | |
translateTo(center0 = p, l); | |
that = d3.select(that); | |
if (duration > 0) that = that.transition().duration(duration); | |
that.call(zoom.event); | |
} | |
function rescale() { | |
if (x1) x1.domain(x0.range().map(function(x) { | |
return (x - view.x) / view.k; | |
}).map(x0.invert)); | |
if (y1) y1.domain(y0.range().map(function(y) { | |
return (y - view.y) / view.k; | |
}).map(y0.invert)); | |
} | |
function zoomstarted(dispatch) { | |
if (!zooming++) dispatch({ | |
type: "zoomstart" | |
}); | |
} | |
function zoomed(dispatch) { | |
rescale(); | |
dispatch({ | |
type: "zoom", | |
scale: view.k, | |
translate: [ view.x, view.y ] | |
}); | |
} | |
function zoomended(dispatch) { | |
if (!--zooming) dispatch({ | |
type: "zoomend" | |
}); | |
center0 = null; | |
} | |
function mousedowned() { | |
var that = this, target = d3.event.target, dispatch = event.of(that, arguments), dragged = 0, subject = d3.select(d3_window(that)).on(mousemove, moved).on(mouseup, ended), location0 = location(d3.mouse(that)), dragRestore = d3_event_dragSuppress(that); | |
d3_selection_interrupt.call(that); | |
zoomstarted(dispatch); | |
function moved() { | |
dragged = 1; | |
translateTo(d3.mouse(that), location0); | |
zoomed(dispatch); | |
} | |
function ended() { | |
subject.on(mousemove, null).on(mouseup, null); | |
dragRestore(dragged && d3.event.target === target); | |
zoomended(dispatch); | |
} | |
} | |
function touchstarted() { | |
var that = this, dispatch = event.of(that, arguments), locations0 = {}, distance0 = 0, scale0, zoomName = ".zoom-" + d3.event.changedTouches[0].identifier, touchmove = "touchmove" + zoomName, touchend = "touchend" + zoomName, targets = [], subject = d3.select(that), dragRestore = d3_event_dragSuppress(that); | |
started(); | |
zoomstarted(dispatch); | |
subject.on(mousedown, null).on(touchstart, started); | |
function relocate() { | |
var touches = d3.touches(that); | |
scale0 = view.k; | |
touches.forEach(function(t) { | |
if (t.identifier in locations0) locations0[t.identifier] = location(t); | |
}); | |
return touches; | |
} | |
function started() { | |
var target = d3.event.target; | |
d3.select(target).on(touchmove, moved).on(touchend, ended); | |
targets.push(target); | |
var changed = d3.event.changedTouches; | |
for (var i = 0, n = changed.length; i < n; ++i) { | |
locations0[changed[i].identifier] = null; | |
} | |
var touches = relocate(), now = Date.now(); | |
if (touches.length === 1) { | |
if (now - touchtime < 500) { | |
var p = touches[0]; | |
zoomTo(that, p, locations0[p.identifier], Math.floor(Math.log(view.k) / Math.LN2) + 1); | |
d3_eventPreventDefault(); | |
} | |
touchtime = now; | |
} else if (touches.length > 1) { | |
var p = touches[0], q = touches[1], dx = p[0] - q[0], dy = p[1] - q[1]; | |
distance0 = dx * dx + dy * dy; | |
} | |
} | |
function moved() { | |
var touches = d3.touches(that), p0, l0, p1, l1; | |
d3_selection_interrupt.call(that); | |
for (var i = 0, n = touches.length; i < n; ++i, l1 = null) { | |
p1 = touches[i]; | |
if (l1 = locations0[p1.identifier]) { | |
if (l0) break; | |
p0 = p1, l0 = l1; | |
} | |
} | |
if (l1) { | |
var distance1 = (distance1 = p1[0] - p0[0]) * distance1 + (distance1 = p1[1] - p0[1]) * distance1, scale1 = distance0 && Math.sqrt(distance1 / distance0); | |
p0 = [ (p0[0] + p1[0]) / 2, (p0[1] + p1[1]) / 2 ]; | |
l0 = [ (l0[0] + l1[0]) / 2, (l0[1] + l1[1]) / 2 ]; | |
scaleTo(scale1 * scale0); | |
} | |
touchtime = null; | |
translateTo(p0, l0); | |
zoomed(dispatch); | |
} | |
function ended() { | |
if (d3.event.touches.length) { | |
var changed = d3.event.changedTouches; | |
for (var i = 0, n = changed.length; i < n; ++i) { | |
delete locations0[changed[i].identifier]; | |
} | |
for (var identifier in locations0) { | |
return void relocate(); | |
} | |
} | |
d3.selectAll(targets).on(zoomName, null); | |
subject.on(mousedown, mousedowned).on(touchstart, touchstarted); | |
dragRestore(); | |
zoomended(dispatch); | |
} | |
} | |
function mousewheeled() { | |
var dispatch = event.of(this, arguments); | |
if (mousewheelTimer) clearTimeout(mousewheelTimer); else translate0 = location(center0 = center || d3.mouse(this)), | |
d3_selection_interrupt.call(this), zoomstarted(dispatch); | |
mousewheelTimer = setTimeout(function() { | |
mousewheelTimer = null; | |
zoomended(dispatch); | |
}, 50); | |
d3_eventPreventDefault(); | |
scaleTo(Math.pow(2, d3_behavior_zoomDelta() * .002) * view.k); | |
translateTo(center0, translate0); | |
zoomed(dispatch); | |
} | |
function dblclicked() { | |
var p = d3.mouse(this), k = Math.log(view.k) / Math.LN2; | |
zoomTo(this, p, location(p), d3.event.shiftKey ? Math.ceil(k) - 1 : Math.floor(k) + 1); | |
} | |
return d3.rebind(zoom, event, "on"); | |
}; | |
var d3_behavior_zoomInfinity = [ 0, Infinity ], d3_behavior_zoomDelta, d3_behavior_zoomWheel; | |
d3.color = d3_color; | |
function d3_color() {} | |
d3_color.prototype.toString = function() { | |
return this.rgb() + ""; | |
}; | |
d3.hsl = d3_hsl; | |
function d3_hsl(h, s, l) { | |
return this instanceof d3_hsl ? void (this.h = +h, this.s = +s, this.l = +l) : arguments.length < 2 ? h instanceof d3_hsl ? new d3_hsl(h.h, h.s, h.l) : d3_rgb_parse("" + h, d3_rgb_hsl, d3_hsl) : new d3_hsl(h, s, l); | |
} | |
var d3_hslPrototype = d3_hsl.prototype = new d3_color(); | |
d3_hslPrototype.brighter = function(k) { | |
k = Math.pow(.7, arguments.length ? k : 1); | |
return new d3_hsl(this.h, this.s, this.l / k); | |
}; | |
d3_hslPrototype.darker = function(k) { | |
k = Math.pow(.7, arguments.length ? k : 1); | |
return new d3_hsl(this.h, this.s, k * this.l); | |
}; | |
d3_hslPrototype.rgb = function() { | |
return d3_hsl_rgb(this.h, this.s, this.l); | |
}; | |
function d3_hsl_rgb(h, s, l) { | |
var m1, m2; | |
h = isNaN(h) ? 0 : (h %= 360) < 0 ? h + 360 : h; | |
s = isNaN(s) ? 0 : s < 0 ? 0 : s > 1 ? 1 : s; | |
l = l < 0 ? 0 : l > 1 ? 1 : l; | |
m2 = l <= .5 ? l * (1 + s) : l + s - l * s; | |
m1 = 2 * l - m2; | |
function v(h) { | |
if (h > 360) h -= 360; else if (h < 0) h += 360; | |
if (h < 60) return m1 + (m2 - m1) * h / 60; | |
if (h < 180) return m2; | |
if (h < 240) return m1 + (m2 - m1) * (240 - h) / 60; | |
return m1; | |
} | |
function vv(h) { | |
return Math.round(v(h) * 255); | |
} | |
return new d3_rgb(vv(h + 120), vv(h), vv(h - 120)); | |
} | |
d3.hcl = d3_hcl; | |
function d3_hcl(h, c, l) { | |
return this instanceof d3_hcl ? void (this.h = +h, this.c = +c, this.l = +l) : arguments.length < 2 ? h instanceof d3_hcl ? new d3_hcl(h.h, h.c, h.l) : h instanceof d3_lab ? d3_lab_hcl(h.l, h.a, h.b) : d3_lab_hcl((h = d3_rgb_lab((h = d3.rgb(h)).r, h.g, h.b)).l, h.a, h.b) : new d3_hcl(h, c, l); | |
} | |
var d3_hclPrototype = d3_hcl.prototype = new d3_color(); | |
d3_hclPrototype.brighter = function(k) { | |
return new d3_hcl(this.h, this.c, Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1))); | |
}; | |
d3_hclPrototype.darker = function(k) { | |
return new d3_hcl(this.h, this.c, Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1))); | |
}; | |
d3_hclPrototype.rgb = function() { | |
return d3_hcl_lab(this.h, this.c, this.l).rgb(); | |
}; | |
function d3_hcl_lab(h, c, l) { | |
if (isNaN(h)) h = 0; | |
if (isNaN(c)) c = 0; | |
return new d3_lab(l, Math.cos(h *= d3_radians) * c, Math.sin(h) * c); | |
} | |
d3.lab = d3_lab; | |
function d3_lab(l, a, b) { | |
return this instanceof d3_lab ? void (this.l = +l, this.a = +a, this.b = +b) : arguments.length < 2 ? l instanceof d3_lab ? new d3_lab(l.l, l.a, l.b) : l instanceof d3_hcl ? d3_hcl_lab(l.h, l.c, l.l) : d3_rgb_lab((l = d3_rgb(l)).r, l.g, l.b) : new d3_lab(l, a, b); | |
} | |
var d3_lab_K = 18; | |
var d3_lab_X = .95047, d3_lab_Y = 1, d3_lab_Z = 1.08883; | |
var d3_labPrototype = d3_lab.prototype = new d3_color(); | |
d3_labPrototype.brighter = function(k) { | |
return new d3_lab(Math.min(100, this.l + d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); | |
}; | |
d3_labPrototype.darker = function(k) { | |
return new d3_lab(Math.max(0, this.l - d3_lab_K * (arguments.length ? k : 1)), this.a, this.b); | |
}; | |
d3_labPrototype.rgb = function() { | |
return d3_lab_rgb(this.l, this.a, this.b); | |
}; | |
function d3_lab_rgb(l, a, b) { | |
var y = (l + 16) / 116, x = y + a / 500, z = y - b / 200; | |
x = d3_lab_xyz(x) * d3_lab_X; | |
y = d3_lab_xyz(y) * d3_lab_Y; | |
z = d3_lab_xyz(z) * d3_lab_Z; | |
return new d3_rgb(d3_xyz_rgb(3.2404542 * x - 1.5371385 * y - .4985314 * z), d3_xyz_rgb(-.969266 * x + 1.8760108 * y + .041556 * z), d3_xyz_rgb(.0556434 * x - .2040259 * y + 1.0572252 * z)); | |
} | |
function d3_lab_hcl(l, a, b) { | |
return l > 0 ? new d3_hcl(Math.atan2(b, a) * d3_degrees, Math.sqrt(a * a + b * b), l) : new d3_hcl(NaN, NaN, l); | |
} | |
function d3_lab_xyz(x) { | |
return x > .206893034 ? x * x * x : (x - 4 / 29) / 7.787037; | |
} | |
function d3_xyz_lab(x) { | |
return x > .008856 ? Math.pow(x, 1 / 3) : 7.787037 * x + 4 / 29; | |
} | |
function d3_xyz_rgb(r) { | |
return Math.round(255 * (r <= .00304 ? 12.92 * r : 1.055 * Math.pow(r, 1 / 2.4) - .055)); | |
} | |
d3.rgb = d3_rgb; | |
function d3_rgb(r, g, b) { | |
return this instanceof d3_rgb ? void (this.r = ~~r, this.g = ~~g, this.b = ~~b) : arguments.length < 2 ? r instanceof d3_rgb ? new d3_rgb(r.r, r.g, r.b) : d3_rgb_parse("" + r, d3_rgb, d3_hsl_rgb) : new d3_rgb(r, g, b); | |
} | |
function d3_rgbNumber(value) { | |
return new d3_rgb(value >> 16, value >> 8 & 255, value & 255); | |
} | |
function d3_rgbString(value) { | |
return d3_rgbNumber(value) + ""; | |
} | |
var d3_rgbPrototype = d3_rgb.prototype = new d3_color(); | |
d3_rgbPrototype.brighter = function(k) { | |
k = Math.pow(.7, arguments.length ? k : 1); | |
var r = this.r, g = this.g, b = this.b, i = 30; | |
if (!r && !g && !b) return new d3_rgb(i, i, i); | |
if (r && r < i) r = i; | |
if (g && g < i) g = i; | |
if (b && b < i) b = i; | |
return new d3_rgb(Math.min(255, r / k), Math.min(255, g / k), Math.min(255, b / k)); | |
}; | |
d3_rgbPrototype.darker = function(k) { | |
k = Math.pow(.7, arguments.length ? k : 1); | |
return new d3_rgb(k * this.r, k * this.g, k * this.b); | |
}; | |
d3_rgbPrototype.hsl = function() { | |
return d3_rgb_hsl(this.r, this.g, this.b); | |
}; | |
d3_rgbPrototype.toString = function() { | |
return "#" + d3_rgb_hex(this.r) + d3_rgb_hex(this.g) + d3_rgb_hex(this.b); | |
}; | |
function d3_rgb_hex(v) { | |
return v < 16 ? "0" + Math.max(0, v).toString(16) : Math.min(255, v).toString(16); | |
} | |
function d3_rgb_parse(format, rgb, hsl) { | |
var r = 0, g = 0, b = 0, m1, m2, color; | |
m1 = /([a-z]+)\((.*)\)/i.exec(format); | |
if (m1) { | |
m2 = m1[2].split(","); | |
switch (m1[1]) { | |
case "hsl": | |
{ | |
return hsl(parseFloat(m2[0]), parseFloat(m2[1]) / 100, parseFloat(m2[2]) / 100); | |
} | |
case "rgb": | |
{ | |
return rgb(d3_rgb_parseNumber(m2[0]), d3_rgb_parseNumber(m2[1]), d3_rgb_parseNumber(m2[2])); | |
} | |
} | |
} | |
if (color = d3_rgb_names.get(format.toLowerCase())) { | |
return rgb(color.r, color.g, color.b); | |
} | |
if (format != null && format.charAt(0) === "#" && !isNaN(color = parseInt(format.slice(1), 16))) { | |
if (format.length === 4) { | |
r = (color & 3840) >> 4; | |
r = r >> 4 | r; | |
g = color & 240; | |
g = g >> 4 | g; | |
b = color & 15; | |
b = b << 4 | b; | |
} else if (format.length === 7) { | |
r = (color & 16711680) >> 16; | |
g = (color & 65280) >> 8; | |
b = color & 255; | |
} | |
} | |
return rgb(r, g, b); | |
} | |
function d3_rgb_hsl(r, g, b) { | |
var min = Math.min(r /= 255, g /= 255, b /= 255), max = Math.max(r, g, b), d = max - min, h, s, l = (max + min) / 2; | |
if (d) { | |
s = l < .5 ? d / (max + min) : d / (2 - max - min); | |
if (r == max) h = (g - b) / d + (g < b ? 6 : 0); else if (g == max) h = (b - r) / d + 2; else h = (r - g) / d + 4; | |
h *= 60; | |
} else { | |
h = NaN; | |
s = l > 0 && l < 1 ? 0 : h; | |
} | |
return new d3_hsl(h, s, l); | |
} | |
function d3_rgb_lab(r, g, b) { | |
r = d3_rgb_xyz(r); | |
g = d3_rgb_xyz(g); | |
b = d3_rgb_xyz(b); | |
var x = d3_xyz_lab((.4124564 * r + .3575761 * g + .1804375 * b) / d3_lab_X), y = d3_xyz_lab((.2126729 * r + .7151522 * g + .072175 * b) / d3_lab_Y), z = d3_xyz_lab((.0193339 * r + .119192 * g + .9503041 * b) / d3_lab_Z); | |
return d3_lab(116 * y - 16, 500 * (x - y), 200 * (y - z)); | |
} | |
function d3_rgb_xyz(r) { | |
return (r /= 255) <= .04045 ? r / 12.92 : Math.pow((r + .055) / 1.055, 2.4); | |
} | |
function d3_rgb_parseNumber(c) { | |
var f = parseFloat(c); | |
return c.charAt(c.length - 1) === "%" ? Math.round(f * 2.55) : f; | |
} | |
var d3_rgb_names = d3.map({ | |
aliceblue: 15792383, | |
antiquewhite: 16444375, | |
aqua: 65535, | |
aquamarine: 8388564, | |
azure: 15794175, | |
beige: 16119260, | |
bisque: 16770244, | |
black: 0, | |
blanchedalmond: 16772045, | |
blue: 255, | |
blueviolet: 9055202, | |
brown: 10824234, | |
burlywood: 14596231, | |
cadetblue: 6266528, | |
chartreuse: 8388352, | |
chocolate: 13789470, | |
coral: 16744272, | |
cornflowerblue: 6591981, | |
cornsilk: 16775388, | |
crimson: 14423100, | |
cyan: 65535, | |
darkblue: 139, | |
darkcyan: 35723, | |
darkgoldenrod: 12092939, | |
darkgray: 11119017, | |
darkgreen: 25600, | |
darkgrey: 11119017, | |
darkkhaki: 12433259, | |
darkmagenta: 9109643, | |
darkolivegreen: 5597999, | |
darkorange: 16747520, | |
darkorchid: 10040012, | |
darkred: 9109504, | |
darksalmon: 15308410, | |
darkseagreen: 9419919, | |
darkslateblue: 4734347, | |
darkslategray: 3100495, | |
darkslategrey: 3100495, | |
darkturquoise: 52945, | |
darkviolet: 9699539, | |
deeppink: 16716947, | |
deepskyblue: 49151, | |
dimgray: 6908265, | |
dimgrey: 6908265, | |
dodgerblue: 2003199, | |
firebrick: 11674146, | |
floralwhite: 16775920, | |
forestgreen: 2263842, | |
fuchsia: 16711935, | |
gainsboro: 14474460, | |
ghostwhite: 16316671, | |
gold: 16766720, | |
goldenrod: 14329120, | |
gray: 8421504, | |
green: 32768, | |
greenyellow: 11403055, | |
grey: 8421504, | |
honeydew: 15794160, | |
hotpink: 16738740, | |
indianred: 13458524, | |
indigo: 4915330, | |
ivory: 16777200, | |
khaki: 15787660, | |
lavender: 15132410, | |
lavenderblush: 16773365, | |
lawngreen: 8190976, | |
lemonchiffon: 16775885, | |
lightblue: 11393254, | |
lightcoral: 15761536, | |
lightcyan: 14745599, | |
lightgoldenrodyellow: 16448210, | |
lightgray: 13882323, | |
lightgreen: 9498256, | |
lightgrey: 13882323, | |
lightpink: 16758465, | |
lightsalmon: 16752762, | |
lightseagreen: 2142890, | |
lightskyblue: 8900346, | |
lightslategray: 7833753, | |
lightslategrey: 7833753, | |
lightsteelblue: 11584734, | |
lightyellow: 16777184, | |
lime: 65280, | |
limegreen: 3329330, | |
linen: 16445670, | |
magenta: 16711935, | |
maroon: 8388608, | |
mediumaquamarine: 6737322, | |
mediumblue: 205, | |
mediumorchid: 12211667, | |
mediumpurple: 9662683, | |
mediumseagreen: 3978097, | |
mediumslateblue: 8087790, | |
mediumspringgreen: 64154, | |
mediumturquoise: 4772300, | |
mediumvioletred: 13047173, | |
midnightblue: 1644912, | |
mintcream: 16121850, | |
mistyrose: 16770273, | |
moccasin: 16770229, | |
navajowhite: 16768685, | |
navy: 128, | |
oldlace: 16643558, | |
olive: 8421376, | |
olivedrab: 7048739, | |
orange: 16753920, | |
orangered: 16729344, | |
orchid: 14315734, | |
palegoldenrod: 15657130, | |
palegreen: 10025880, | |
paleturquoise: 11529966, | |
palevioletred: 14381203, | |
papayawhip: 16773077, | |
peachpuff: 16767673, | |
peru: 13468991, | |
pink: 16761035, | |
plum: 14524637, | |
powderblue: 11591910, | |
purple: 8388736, | |
rebeccapurple: 6697881, | |
red: 16711680, | |
rosybrown: 12357519, | |
royalblue: 4286945, | |
saddlebrown: 9127187, | |
salmon: 16416882, | |
sandybrown: 16032864, | |
seagreen: 3050327, | |
seashell: 16774638, | |
sienna: 10506797, | |
silver: 12632256, | |
skyblue: 8900331, | |
slateblue: 6970061, | |
slategray: 7372944, | |
slategrey: 7372944, | |
snow: 16775930, | |
springgreen: 65407, | |
steelblue: 4620980, | |
tan: 13808780, | |
teal: 32896, | |
thistle: 14204888, | |
tomato: 16737095, | |
turquoise: 4251856, | |
violet: 15631086, | |
wheat: 16113331, | |
white: 16777215, | |
whitesmoke: 16119285, | |
yellow: 16776960, | |
yellowgreen: 10145074 | |
}); | |
d3_rgb_names.forEach(function(key, value) { | |
d3_rgb_names.set(key, d3_rgbNumber(value)); | |
}); | |
function d3_functor(v) { | |
return typeof v === "function" ? v : function() { | |
return v; | |
}; | |
} | |
d3.functor = d3_functor; | |
d3.xhr = d3_xhrType(d3_identity); | |
function d3_xhrType(response) { | |
return function(url, mimeType, callback) { | |
if (arguments.length === 2 && typeof mimeType === "function") callback = mimeType, | |
mimeType = null; | |
return d3_xhr(url, mimeType, response, callback); | |
}; | |
} | |
function d3_xhr(url, mimeType, response, callback) { | |
var xhr = {}, dispatch = d3.dispatch("beforesend", "progress", "load", "error"), headers = {}, request = new XMLHttpRequest(), responseType = null; | |
if (this.XDomainRequest && !("withCredentials" in request) && /^(http(s)?:)?\/\//.test(url)) request = new XDomainRequest(); | |
"onload" in request ? request.onload = request.onerror = respond : request.onreadystatechange = function() { | |
request.readyState > 3 && respond(); | |
}; | |
function respond() { | |
var status = request.status, result; | |
if (!status && d3_xhrHasResponse(request) || status >= 200 && status < 300 || status === 304) { | |
try { | |
result = response.call(xhr, request); | |
} catch (e) { | |
dispatch.error.call(xhr, e); | |
return; | |
} | |
dispatch.load.call(xhr, result); | |
} else { | |
dispatch.error.call(xhr, request); | |
} | |
} | |
request.onprogress = function(event) { | |
var o = d3.event; | |
d3.event = event; | |
try { | |
dispatch.progress.call(xhr, request); | |
} finally { | |
d3.event = o; | |
} | |
}; | |
xhr.header = function(name, value) { | |
name = (name + "").toLowerCase(); | |
if (arguments.length < 2) return headers[name]; | |
if (value == null) delete headers[name]; else headers[name] = value + ""; | |
return xhr; | |
}; | |
xhr.mimeType = function(value) { | |
if (!arguments.length) return mimeType; | |
mimeType = value == null ? null : value + ""; | |
return xhr; | |
}; | |
xhr.responseType = function(value) { | |
if (!arguments.length) return responseType; | |
responseType = value; | |
return xhr; | |
}; | |
xhr.response = function(value) { | |
response = value; | |
return xhr; | |
}; | |
[ "get", "post" ].forEach(function(method) { | |
xhr[method] = function() { | |
return xhr.send.apply(xhr, [ method ].concat(d3_array(arguments))); | |
}; | |
}); | |
xhr.send = function(method, data, callback) { | |
if (arguments.length === 2 && typeof data === "function") callback = data, data = null; | |
request.open(method, url, true); | |
if (mimeType != null && !("accept" in headers)) headers["accept"] = mimeType + ",*/*"; | |
if (request.setRequestHeader) for (var name in headers) request.setRequestHeader(name, headers[name]); | |
if (mimeType != null && request.overrideMimeType) request.overrideMimeType(mimeType); | |
if (responseType != null) request.responseType = responseType; | |
if (callback != null) xhr.on("error", callback).on("load", function(request) { | |
callback(null, request); | |
}); | |
dispatch.beforesend.call(xhr, request); | |
request.send(data == null ? null : data); | |
return xhr; | |
}; | |
xhr.abort = function() { | |
request.abort(); | |
return xhr; | |
}; | |
d3.rebind(xhr, dispatch, "on"); | |
return callback == null ? xhr : xhr.get(d3_xhr_fixCallback(callback)); | |
} | |
function d3_xhr_fixCallback(callback) { | |
return callback.length === 1 ? function(error, request) { | |
callback(error == null ? request : null); | |
} : callback; | |
} | |
function d3_xhrHasResponse(request) { | |
var type = request.responseType; | |
return type && type !== "text" ? request.response : request.responseText; | |
} | |
d3.dsv = function(delimiter, mimeType) { | |
var reFormat = new RegExp('["' + delimiter + "\n]"), delimiterCode = delimiter.charCodeAt(0); | |
function dsv(url, row, callback) { | |
if (arguments.length < 3) callback = row, row = null; | |
var xhr = d3_xhr(url, mimeType, row == null ? response : typedResponse(row), callback); | |
xhr.row = function(_) { | |
return arguments.length ? xhr.response((row = _) == null ? response : typedResponse(_)) : row; | |
}; | |
return xhr; | |
} | |
function response(request) { | |
return dsv.parse(request.responseText); | |
} | |
function typedResponse(f) { | |
return function(request) { | |
return dsv.parse(request.responseText, f); | |
}; | |
} | |
dsv.parse = function(text, f) { | |
var o; | |
return dsv.parseRows(text, function(row, i) { | |
if (o) return o(row, i - 1); | |
var a = new Function("d", "return {" + row.map(function(name, i) { | |
return JSON.stringify(name) + ": d[" + i + "]"; | |
}).join(",") + "}"); | |
o = f ? function(row, i) { | |
return f(a(row), i); | |
} : a; | |
}); | |
}; | |
dsv.parseRows = function(text, f) { | |
var EOL = {}, EOF = {}, rows = [], N = text.length, I = 0, n = 0, t, eol; | |
function token() { | |
if (I >= N) return EOF; | |
if (eol) return eol = false, EOL; | |
var j = I; | |
if (text.charCodeAt(j) === 34) { | |
var i = j; | |
while (i++ < N) { | |
if (text.charCodeAt(i) === 34) { | |
if (text.charCodeAt(i + 1) !== 34) break; | |
++i; | |
} | |
} | |
I = i + 2; | |
var c = text.charCodeAt(i + 1); | |
if (c === 13) { | |
eol = true; | |
if (text.charCodeAt(i + 2) === 10) ++I; | |
} else if (c === 10) { | |
eol = true; | |
} | |
return text.slice(j + 1, i).replace(/""/g, '"'); | |
} | |
while (I < N) { | |
var c = text.charCodeAt(I++), k = 1; | |
if (c === 10) eol = true; else if (c === 13) { | |
eol = true; | |
if (text.charCodeAt(I) === 10) ++I, ++k; | |
} else if (c !== delimiterCode) continue; | |
return text.slice(j, I - k); | |
} | |
return text.slice(j); | |
} | |
while ((t = token()) !== EOF) { | |
var a = []; | |
while (t !== EOL && t !== EOF) { | |
a.push(t); | |
t = token(); | |
} | |
if (f && (a = f(a, n++)) == null) continue; | |
rows.push(a); | |
} | |
return rows; | |
}; | |
dsv.format = function(rows) { | |
if (Array.isArray(rows[0])) return dsv.formatRows(rows); | |
var fieldSet = new d3_Set(), fields = []; | |
rows.forEach(function(row) { | |
for (var field in row) { | |
if (!fieldSet.has(field)) { | |
fields.push(fieldSet.add(field)); | |
} | |
} | |
}); | |
return [ fields.map(formatValue).join(delimiter) ].concat(rows.map(function(row) { | |
return fields.map(function(field) { | |
return formatValue(row[field]); | |
}).join(delimiter); | |
})).join("\n"); | |
}; | |
dsv.formatRows = function(rows) { | |
return rows.map(formatRow).join("\n"); | |
}; | |
function formatRow(row) { | |
return row.map(formatValue).join(delimiter); | |
} | |
function formatValue(text) { | |
return reFormat.test(text) ? '"' + text.replace(/\"/g, '""') + '"' : text; | |
} | |
return dsv; | |
}; | |
d3.csv = d3.dsv(",", "text/csv"); | |
d3.tsv = d3.dsv(" ", "text/tab-separated-values"); | |
var d3_timer_queueHead, d3_timer_queueTail, d3_timer_interval, d3_timer_timeout, d3_timer_active, d3_timer_frame = this[d3_vendorSymbol(this, "requestAnimationFrame")] || function(callback) { | |
setTimeout(callback, 17); | |
}; | |
d3.timer = function(callback, delay, then) { | |
var n = arguments.length; | |
if (n < 2) delay = 0; | |
if (n < 3) then = Date.now(); | |
var time = then + delay, timer = { | |
c: callback, | |
t: time, | |
f: false, | |
n: null | |
}; | |
if (d3_timer_queueTail) d3_timer_queueTail.n = timer; else d3_timer_queueHead = timer; | |
d3_timer_queueTail = timer; | |
if (!d3_timer_interval) { | |
d3_timer_timeout = clearTimeout(d3_timer_timeout); | |
d3_timer_interval = 1; | |
d3_timer_frame(d3_timer_step); | |
} | |
}; | |
function d3_timer_step() { | |
var now = d3_timer_mark(), delay = d3_timer_sweep() - now; | |
if (delay > 24) { | |
if (isFinite(delay)) { | |
clearTimeout(d3_timer_timeout); | |
d3_timer_timeout = setTimeout(d3_timer_step, delay); | |
} | |
d3_timer_interval = 0; | |
} else { | |
d3_timer_interval = 1; | |
d3_timer_frame(d3_timer_step); | |
} | |
} | |
d3.timer.flush = function() { | |
d3_timer_mark(); | |
d3_timer_sweep(); | |
}; | |
function d3_timer_mark() { | |
var now = Date.now(); | |
d3_timer_active = d3_timer_queueHead; | |
while (d3_timer_active) { | |
if (now >= d3_timer_active.t) d3_timer_active.f = d3_timer_active.c(now - d3_timer_active.t); | |
d3_timer_active = d3_timer_active.n; | |
} | |
return now; | |
} | |
function d3_timer_sweep() { | |
var t0, t1 = d3_timer_queueHead, time = Infinity; | |
while (t1) { | |
if (t1.f) { | |
t1 = t0 ? t0.n = t1.n : d3_timer_queueHead = t1.n; | |
} else { | |
if (t1.t < time) time = t1.t; | |
t1 = (t0 = t1).n; | |
} | |
} | |
d3_timer_queueTail = t0; | |
return time; | |
} | |
function d3_format_precision(x, p) { | |
return p - (x ? Math.ceil(Math.log(x) / Math.LN10) : 1); | |
} | |
d3.round = function(x, n) { | |
return n ? Math.round(x * (n = Math.pow(10, n))) / n : Math.round(x); | |
}; | |
var d3_formatPrefixes = [ "y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y" ].map(d3_formatPrefix); | |
d3.formatPrefix = function(value, precision) { | |
var i = 0; | |
if (value) { | |
if (value < 0) value *= -1; | |
if (precision) value = d3.round(value, d3_format_precision(value, precision)); | |
i = 1 + Math.floor(1e-12 + Math.log(value) / Math.LN10); | |
i = Math.max(-24, Math.min(24, Math.floor((i - 1) / 3) * 3)); | |
} | |
return d3_formatPrefixes[8 + i / 3]; | |
}; | |
function d3_formatPrefix(d, i) { | |
var k = Math.pow(10, abs(8 - i) * 3); | |
return { | |
scale: i > 8 ? function(d) { | |
return d / k; | |
} : function(d) { | |
return d * k; | |
}, | |
symbol: d | |
}; | |
} | |
function d3_locale_numberFormat(locale) { | |
var locale_decimal = locale.decimal, locale_thousands = locale.thousands, locale_grouping = locale.grouping, locale_currency = locale.currency, formatGroup = locale_grouping && locale_thousands ? function(value, width) { | |
var i = value.length, t = [], j = 0, g = locale_grouping[0], length = 0; | |
while (i > 0 && g > 0) { | |
if (length + g + 1 > width) g = Math.max(1, width - length); | |
t.push(value.substring(i -= g, i + g)); | |
if ((length += g + 1) > width) break; | |
g = locale_grouping[j = (j + 1) % locale_grouping.length]; | |
} | |
return t.reverse().join(locale_thousands); | |
} : d3_identity; | |
return function(specifier) { | |
var match = d3_format_re.exec(specifier), fill = match[1] || " ", align = match[2] || ">", sign = match[3] || "-", symbol = match[4] || "", zfill = match[5], width = +match[6], comma = match[7], precision = match[8], type = match[9], scale = 1, prefix = "", suffix = "", integer = false, exponent = true; | |
if (precision) precision = +precision.substring(1); | |
if (zfill || fill === "0" && align === "=") { | |
zfill = fill = "0"; | |
align = "="; | |
} | |
switch (type) { | |
case "n": | |
comma = true; | |
type = "g"; | |
break; | |
case "%": | |
scale = 100; | |
suffix = "%"; | |
type = "f"; | |
break; | |
case "p": | |
scale = 100; | |
suffix = "%"; | |
type = "r"; | |
break; | |
case "b": | |
case "o": | |
case "x": | |
case "X": | |
if (symbol === "#") prefix = "0" + type.toLowerCase(); | |
case "c": | |
exponent = false; | |
case "d": | |
integer = true; | |
precision = 0; | |
break; | |
case "s": | |
scale = -1; | |
type = "r"; | |
break; | |
} | |
if (symbol === "$") prefix = locale_currency[0], suffix = locale_currency[1]; | |
if (type == "r" && !precision) type = "g"; | |
if (precision != null) { | |
if (type == "g") precision = Math.max(1, Math.min(21, precision)); else if (type == "e" || type == "f") precision = Math.max(0, Math.min(20, precision)); | |
} | |
type = d3_format_types.get(type) || d3_format_typeDefault; | |
var zcomma = zfill && comma; | |
return function(value) { | |
var fullSuffix = suffix; | |
if (integer && value % 1) return ""; | |
var negative = value < 0 || value === 0 && 1 / value < 0 ? (value = -value, "-") : sign === "-" ? "" : sign; | |
if (scale < 0) { | |
var unit = d3.formatPrefix(value, precision); | |
value = unit.scale(value); | |
fullSuffix = unit.symbol + suffix; | |
} else { | |
value *= scale; | |
} | |
value = type(value, precision); | |
var i = value.lastIndexOf("."), before, after; | |
if (i < 0) { | |
var j = exponent ? value.lastIndexOf("e") : -1; | |
if (j < 0) before = value, after = ""; else before = value.substring(0, j), after = value.substring(j); | |
} else { | |
before = value.substring(0, i); | |
after = locale_decimal + value.substring(i + 1); | |
} | |
if (!zfill && comma) before = formatGroup(before, Infinity); | |
var length = prefix.length + before.length + after.length + (zcomma ? 0 : negative.length), padding = length < width ? new Array(length = width - length + 1).join(fill) : ""; | |
if (zcomma) before = formatGroup(padding + before, padding.length ? width - after.length : Infinity); | |
negative += prefix; | |
value = before + after; | |
return (align === "<" ? negative + value + padding : align === ">" ? padding + negative + value : align === "^" ? padding.substring(0, length >>= 1) + negative + value + padding.substring(length) : negative + (zcomma ? value : padding + value)) + fullSuffix; | |
}; | |
}; | |
} | |
var d3_format_re = /(?:([^{])?([<>=^]))?([+\- ])?([$#])?(0)?(\d+)?(,)?(\.-?\d+)?([a-z%])?/i; | |
var d3_format_types = d3.map({ | |
b: function(x) { | |
return x.toString(2); | |
}, | |
c: function(x) { | |
return String.fromCharCode(x); | |
}, | |
o: function(x) { | |
return x.toString(8); | |
}, | |
x: function(x) { | |
return x.toString(16); | |
}, | |
X: function(x) { | |
return x.toString(16).toUpperCase(); | |
}, | |
g: function(x, p) { | |
return x.toPrecision(p); | |
}, | |
e: function(x, p) { | |
return x.toExponential(p); | |
}, | |
f: function(x, p) { | |
return x.toFixed(p); | |
}, | |
r: function(x, p) { | |
return (x = d3.round(x, d3_format_precision(x, p))).toFixed(Math.max(0, Math.min(20, d3_format_precision(x * (1 + 1e-15), p)))); | |
} | |
}); | |
function d3_format_typeDefault(x) { | |
return x + ""; | |
} | |
var d3_time = d3.time = {}, d3_date = Date; | |
function d3_date_utc() { | |
this._ = new Date(arguments.length > 1 ? Date.UTC.apply(this, arguments) : arguments[0]); | |
} | |
d3_date_utc.prototype = { | |
getDate: function() { | |
return this._.getUTCDate(); | |
}, | |
getDay: function() { | |
return this._.getUTCDay(); | |
}, | |
getFullYear: function() { | |
return this._.getUTCFullYear(); | |
}, | |
getHours: function() { | |
return this._.getUTCHours(); | |
}, | |
getMilliseconds: function() { | |
return this._.getUTCMilliseconds(); | |
}, | |
getMinutes: function() { | |
return this._.getUTCMinutes(); | |
}, | |
getMonth: function() { | |
return this._.getUTCMonth(); | |
}, | |
getSeconds: function() { | |
return this._.getUTCSeconds(); | |
}, | |
getTime: function() { | |
return this._.getTime(); | |
}, | |
getTimezoneOffset: function() { | |
return 0; | |
}, | |
valueOf: function() { | |
return this._.valueOf(); | |
}, | |
setDate: function() { | |
d3_time_prototype.setUTCDate.apply(this._, arguments); | |
}, | |
setDay: function() { | |
d3_time_prototype.setUTCDay.apply(this._, arguments); | |
}, | |
setFullYear: function() { | |
d3_time_prototype.setUTCFullYear.apply(this._, arguments); | |
}, | |
setHours: function() { | |
d3_time_prototype.setUTCHours.apply(this._, arguments); | |
}, | |
setMilliseconds: function() { | |
d3_time_prototype.setUTCMilliseconds.apply(this._, arguments); | |
}, | |
setMinutes: function() { | |
d3_time_prototype.setUTCMinutes.apply(this._, arguments); | |
}, | |
setMonth: function() { | |
d3_time_prototype.setUTCMonth.apply(this._, arguments); | |
}, | |
setSeconds: function() { | |
d3_time_prototype.setUTCSeconds.apply(this._, arguments); | |
}, | |
setTime: function() { | |
d3_time_prototype.setTime.apply(this._, arguments); | |
} | |
}; | |
var d3_time_prototype = Date.prototype; | |
function d3_time_interval(local, step, number) { | |
function round(date) { | |
var d0 = local(date), d1 = offset(d0, 1); | |
return date - d0 < d1 - date ? d0 : d1; | |
} | |
function ceil(date) { | |
step(date = local(new d3_date(date - 1)), 1); | |
return date; | |
} | |
function offset(date, k) { | |
step(date = new d3_date(+date), k); | |
return date; | |
} | |
function range(t0, t1, dt) { | |
var time = ceil(t0), times = []; | |
if (dt > 1) { | |
while (time < t1) { | |
if (!(number(time) % dt)) times.push(new Date(+time)); | |
step(time, 1); | |
} | |
} else { | |
while (time < t1) times.push(new Date(+time)), step(time, 1); | |
} | |
return times; | |
} | |
function range_utc(t0, t1, dt) { | |
try { | |
d3_date = d3_date_utc; | |
var utc = new d3_date_utc(); | |
utc._ = t0; | |
return range(utc, t1, dt); | |
} finally { | |
d3_date = Date; | |
} | |
} | |
local.floor = local; | |
local.round = round; | |
local.ceil = ceil; | |
local.offset = offset; | |
local.range = range; | |
var utc = local.utc = d3_time_interval_utc(local); | |
utc.floor = utc; | |
utc.round = d3_time_interval_utc(round); | |
utc.ceil = d3_time_interval_utc(ceil); | |
utc.offset = d3_time_interval_utc(offset); | |
utc.range = range_utc; | |
return local; | |
} | |
function d3_time_interval_utc(method) { | |
return function(date, k) { | |
try { | |
d3_date = d3_date_utc; | |
var utc = new d3_date_utc(); | |
utc._ = date; | |
return method(utc, k)._; | |
} finally { | |
d3_date = Date; | |
} | |
}; | |
} | |
d3_time.year = d3_time_interval(function(date) { | |
date = d3_time.day(date); | |
date.setMonth(0, 1); | |
return date; | |
}, function(date, offset) { | |
date.setFullYear(date.getFullYear() + offset); | |
}, function(date) { | |
return date.getFullYear(); | |
}); | |
d3_time.years = d3_time.year.range; | |
d3_time.years.utc = d3_time.year.utc.range; | |
d3_time.day = d3_time_interval(function(date) { | |
var day = new d3_date(2e3, 0); | |
day.setFullYear(date.getFullYear(), date.getMonth(), date.getDate()); | |
return day; | |
}, function(date, offset) { | |
date.setDate(date.getDate() + offset); | |
}, function(date) { | |
return date.getDate() - 1; | |
}); | |
d3_time.days = d3_time.day.range; | |
d3_time.days.utc = d3_time.day.utc.range; | |
d3_time.dayOfYear = function(date) { | |
var year = d3_time.year(date); | |
return Math.floor((date - year - (date.getTimezoneOffset() - year.getTimezoneOffset()) * 6e4) / 864e5); | |
}; | |
[ "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" ].forEach(function(day, i) { | |
i = 7 - i; | |
var interval = d3_time[day] = d3_time_interval(function(date) { | |
(date = d3_time.day(date)).setDate(date.getDate() - (date.getDay() + i) % 7); | |
return date; | |
}, function(date, offset) { | |
date.setDate(date.getDate() + Math.floor(offset) * 7); | |
}, function(date) { | |
var day = d3_time.year(date).getDay(); | |
return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7) - (day !== i); | |
}); | |
d3_time[day + "s"] = interval.range; | |
d3_time[day + "s"].utc = interval.utc.range; | |
d3_time[day + "OfYear"] = function(date) { | |
var day = d3_time.year(date).getDay(); | |
return Math.floor((d3_time.dayOfYear(date) + (day + i) % 7) / 7); | |
}; | |
}); | |
d3_time.week = d3_time.sunday; | |
d3_time.weeks = d3_time.sunday.range; | |
d3_time.weeks.utc = d3_time.sunday.utc.range; | |
d3_time.weekOfYear = d3_time.sundayOfYear; | |
function d3_locale_timeFormat(locale) { | |
var locale_dateTime = locale.dateTime, locale_date = locale.date, locale_time = locale.time, locale_periods = locale.periods, locale_days = locale.days, locale_shortDays = locale.shortDays, locale_months = locale.months, locale_shortMonths = locale.shortMonths; | |
function d3_time_format(template) { | |
var n = template.length; | |
function format(date) { | |
var string = [], i = -1, j = 0, c, p, f; | |
while (++i < n) { | |
if (template.charCodeAt(i) === 37) { | |
string.push(template.slice(j, i)); | |
if ((p = d3_time_formatPads[c = template.charAt(++i)]) != null) c = template.charAt(++i); | |
if (f = d3_time_formats[c]) c = f(date, p == null ? c === "e" ? " " : "0" : p); | |
string.push(c); | |
j = i + 1; | |
} | |
} | |
string.push(template.slice(j, i)); | |
return string.join(""); | |
} | |
format.parse = function(string) { | |
var d = { | |
y: 1900, | |
m: 0, | |
d: 1, | |
H: 0, | |
M: 0, | |
S: 0, | |
L: 0, | |
Z: null | |
}, i = d3_time_parse(d, template, string, 0); | |
if (i != string.length) return null; | |
if ("p" in d) d.H = d.H % 12 + d.p * 12; | |
var localZ = d.Z != null && d3_date !== d3_date_utc, date = new (localZ ? d3_date_utc : d3_date)(); | |
if ("j" in d) date.setFullYear(d.y, 0, d.j); else if ("w" in d && ("W" in d || "U" in d)) { | |
date.setFullYear(d.y, 0, 1); | |
date.setFullYear(d.y, 0, "W" in d ? (d.w + 6) % 7 + d.W * 7 - (date.getDay() + 5) % 7 : d.w + d.U * 7 - (date.getDay() + 6) % 7); | |
} else date.setFullYear(d.y, d.m, d.d); | |
date.setHours(d.H + (d.Z / 100 | 0), d.M + d.Z % 100, d.S, d.L); | |
return localZ ? date._ : date; | |
}; | |
format.toString = function() { | |
return template; | |
}; | |
return format; | |
} | |
function d3_time_parse(date, template, string, j) { | |
var c, p, t, i = 0, n = template.length, m = string.length; | |
while (i < n) { | |
if (j >= m) return -1; | |
c = template.charCodeAt(i++); | |
if (c === 37) { | |
t = template.charAt(i++); | |
p = d3_time_parsers[t in d3_time_formatPads ? template.charAt(i++) : t]; | |
if (!p || (j = p(date, string, j)) < 0) return -1; | |
} else if (c != string.charCodeAt(j++)) { | |
return -1; | |
} | |
} | |
return j; | |
} | |
d3_time_format.utc = function(template) { | |
var local = d3_time_format(template); | |
function format(date) { | |
try { | |
d3_date = d3_date_utc; | |
var utc = new d3_date(); | |
utc._ = date; | |
return local(utc); | |
} finally { | |
d3_date = Date; | |
} | |
} | |
format.parse = function(string) { | |
try { | |
d3_date = d3_date_utc; | |
var date = local.parse(string); | |
return date && date._; | |
} finally { | |
d3_date = Date; | |
} | |
}; | |
format.toString = local.toString; | |
return format; | |
}; | |
d3_time_format.multi = d3_time_format.utc.multi = d3_time_formatMulti; | |
var d3_time_periodLookup = d3.map(), d3_time_dayRe = d3_time_formatRe(locale_days), d3_time_dayLookup = d3_time_formatLookup(locale_days), d3_time_dayAbbrevRe = d3_time_formatRe(locale_shortDays), d3_time_dayAbbrevLookup = d3_time_formatLookup(locale_shortDays), d3_time_monthRe = d3_time_formatRe(locale_months), d3_time_monthLookup = d3_time_formatLookup(locale_months), d3_time_monthAbbrevRe = d3_time_formatRe(locale_shortMonths), d3_time_monthAbbrevLookup = d3_time_formatLookup(locale_shortMonths); | |
locale_periods.forEach(function(p, i) { | |
d3_time_periodLookup.set(p.toLowerCase(), i); | |
}); | |
var d3_time_formats = { | |
a: function(d) { | |
return locale_shortDays[d.getDay()]; | |
}, | |
A: function(d) { | |
return locale_days[d.getDay()]; | |
}, | |
b: function(d) { | |
return locale_shortMonths[d.getMonth()]; | |
}, | |
B: function(d) { | |
return locale_months[d.getMonth()]; | |
}, | |
c: d3_time_format(locale_dateTime), | |
d: function(d, p) { | |
return d3_time_formatPad(d.getDate(), p, 2); | |
}, | |
e: function(d, p) { | |
return d3_time_formatPad(d.getDate(), p, 2); | |
}, | |
H: function(d, p) { | |
return d3_time_formatPad(d.getHours(), p, 2); | |
}, | |
I: function(d, p) { | |
return d3_time_formatPad(d.getHours() % 12 || 12, p, 2); | |
}, | |
j: function(d, p) { | |
return d3_time_formatPad(1 + d3_time.dayOfYear(d), p, 3); | |
}, | |
L: function(d, p) { | |
return d3_time_formatPad(d.getMilliseconds(), p, 3); | |
}, | |
m: function(d, p) { | |
return d3_time_formatPad(d.getMonth() + 1, p, 2); | |
}, | |
M: function(d, p) { | |
return d3_time_formatPad(d.getMinutes(), p, 2); | |
}, | |
p: function(d) { | |
return locale_periods[+(d.getHours() >= 12)]; | |
}, | |
S: function(d, p) { | |
return d3_time_formatPad(d.getSeconds(), p, 2); | |
}, | |
U: function(d, p) { | |
return d3_time_formatPad(d3_time.sundayOfYear(d), p, 2); | |
}, | |
w: function(d) { | |
return d.getDay(); | |
}, | |
W: function(d, p) { | |
return d3_time_formatPad(d3_time.mondayOfYear(d), p, 2); | |
}, | |
x: d3_time_format(locale_date), | |
X: d3_time_format(locale_time), | |
y: function(d, p) { | |
return d3_time_formatPad(d.getFullYear() % 100, p, 2); | |
}, | |
Y: function(d, p) { | |
return d3_time_formatPad(d.getFullYear() % 1e4, p, 4); | |
}, | |
Z: d3_time_zone, | |
"%": function() { | |
return "%"; | |
} | |
}; | |
var d3_time_parsers = { | |
a: d3_time_parseWeekdayAbbrev, | |
A: d3_time_parseWeekday, | |
b: d3_time_parseMonthAbbrev, | |
B: d3_time_parseMonth, | |
c: d3_time_parseLocaleFull, | |
d: d3_time_parseDay, | |
e: d3_time_parseDay, | |
H: d3_time_parseHour24, | |
I: d3_time_parseHour24, | |
j: d3_time_parseDayOfYear, | |
L: d3_time_parseMilliseconds, | |
m: d3_time_parseMonthNumber, | |
M: d3_time_parseMinutes, | |
p: d3_time_parseAmPm, | |
S: d3_time_parseSeconds, | |
U: d3_time_parseWeekNumberSunday, | |
w: d3_time_parseWeekdayNumber, | |
W: d3_time_parseWeekNumberMonday, | |
x: d3_time_parseLocaleDate, | |
X: d3_time_parseLocaleTime, | |
y: d3_time_parseYear, | |
Y: d3_time_parseFullYear, | |
Z: d3_time_parseZone, | |
"%": d3_time_parseLiteralPercent | |
}; | |
function d3_time_parseWeekdayAbbrev(date, string, i) { | |
d3_time_dayAbbrevRe.lastIndex = 0; | |
var n = d3_time_dayAbbrevRe.exec(string.slice(i)); | |
return n ? (date.w = d3_time_dayAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; | |
} | |
function d3_time_parseWeekday(date, string, i) { | |
d3_time_dayRe.lastIndex = 0; | |
var n = d3_time_dayRe.exec(string.slice(i)); | |
return n ? (date.w = d3_time_dayLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; | |
} | |
function d3_time_parseMonthAbbrev(date, string, i) { | |
d3_time_monthAbbrevRe.lastIndex = 0; | |
var n = d3_time_monthAbbrevRe.exec(string.slice(i)); | |
return n ? (date.m = d3_time_monthAbbrevLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; | |
} | |
function d3_time_parseMonth(date, string, i) { | |
d3_time_monthRe.lastIndex = 0; | |
var n = d3_time_monthRe.exec(string.slice(i)); | |
return n ? (date.m = d3_time_monthLookup.get(n[0].toLowerCase()), i + n[0].length) : -1; | |
} | |
function d3_time_parseLocaleFull(date, string, i) { | |
return d3_time_parse(date, d3_time_formats.c.toString(), string, i); | |
} | |
function d3_time_parseLocaleDate(date, string, i) { | |
return d3_time_parse(date, d3_time_formats.x.toString(), string, i); | |
} | |
function d3_time_parseLocaleTime(date, string, i) { | |
return d3_time_parse(date, d3_time_formats.X.toString(), string, i); | |
} | |
function d3_time_parseAmPm(date, string, i) { | |
var n = d3_time_periodLookup.get(string.slice(i, i += 2).toLowerCase()); | |
return n == null ? -1 : (date.p = n, i); | |
} | |
return d3_time_format; | |
} | |
var d3_time_formatPads = { | |
"-": "", | |
_: " ", | |
"0": "0" | |
}, d3_time_numberRe = /^\s*\d+/, d3_time_percentRe = /^%/; | |
function d3_time_formatPad(value, fill, width) { | |
var sign = value < 0 ? "-" : "", string = (sign ? -value : value) + "", length = string.length; | |
return sign + (length < width ? new Array(width - length + 1).join(fill) + string : string); | |
} | |
function d3_time_formatRe(names) { | |
return new RegExp("^(?:" + names.map(d3.requote).join("|") + ")", "i"); | |
} | |
function d3_time_formatLookup(names) { | |
var map = new d3_Map(), i = -1, n = names.length; | |
while (++i < n) map.set(names[i].toLowerCase(), i); | |
return map; | |
} | |
function d3_time_parseWeekdayNumber(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 1)); | |
return n ? (date.w = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseWeekNumberSunday(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i)); | |
return n ? (date.U = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseWeekNumberMonday(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i)); | |
return n ? (date.W = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseFullYear(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 4)); | |
return n ? (date.y = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseYear(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.y = d3_time_expandYear(+n[0]), i + n[0].length) : -1; | |
} | |
function d3_time_parseZone(date, string, i) { | |
return /^[+-]\d{4}$/.test(string = string.slice(i, i + 5)) ? (date.Z = -string, | |
i + 5) : -1; | |
} | |
function d3_time_expandYear(d) { | |
return d + (d > 68 ? 1900 : 2e3); | |
} | |
function d3_time_parseMonthNumber(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.m = n[0] - 1, i + n[0].length) : -1; | |
} | |
function d3_time_parseDay(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.d = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseDayOfYear(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 3)); | |
return n ? (date.j = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseHour24(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.H = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseMinutes(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.M = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseSeconds(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 2)); | |
return n ? (date.S = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_parseMilliseconds(date, string, i) { | |
d3_time_numberRe.lastIndex = 0; | |
var n = d3_time_numberRe.exec(string.slice(i, i + 3)); | |
return n ? (date.L = +n[0], i + n[0].length) : -1; | |
} | |
function d3_time_zone(d) { | |
var z = d.getTimezoneOffset(), zs = z > 0 ? "-" : "+", zh = abs(z) / 60 | 0, zm = abs(z) % 60; | |
return zs + d3_time_formatPad(zh, "0", 2) + d3_time_formatPad(zm, "0", 2); | |
} | |
function d3_time_parseLiteralPercent(date, string, i) { | |
d3_time_percentRe.lastIndex = 0; | |
var n = d3_time_percentRe.exec(string.slice(i, i + 1)); | |
return n ? i + n[0].length : -1; | |
} | |
function d3_time_formatMulti(formats) { | |
var n = formats.length, i = -1; | |
while (++i < n) formats[i][0] = this(formats[i][0]); | |
return function(date) { | |
var i = 0, f = formats[i]; | |
while (!f[1](date)) f = formats[++i]; | |
return f[0](date); | |
}; | |
} | |
d3.locale = function(locale) { | |
return { | |
numberFormat: d3_locale_numberFormat(locale), | |
timeFormat: d3_locale_timeFormat(locale) | |
}; | |
}; | |
var d3_locale_enUS = d3.locale({ | |
decimal: ".", | |
thousands: ",", | |
grouping: [ 3 ], | |
currency: [ "$", "" ], | |
dateTime: "%a %b %e %X %Y", | |
date: "%m/%d/%Y", | |
time: "%H:%M:%S", | |
periods: [ "AM", "PM" ], | |
days: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ], | |
shortDays: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ], | |
months: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ], | |
shortMonths: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ] | |
}); | |
d3.format = d3_locale_enUS.numberFormat; | |
d3.geo = {}; | |
function d3_adder() {} | |
d3_adder.prototype = { | |
s: 0, | |
t: 0, | |
add: function(y) { | |
d3_adderSum(y, this.t, d3_adderTemp); | |
d3_adderSum(d3_adderTemp.s, this.s, this); | |
if (this.s) this.t += d3_adderTemp.t; else this.s = d3_adderTemp.t; | |
}, | |
reset: function() { | |
this.s = this.t = 0; | |
}, | |
valueOf: function() { | |
return this.s; | |
} | |
}; | |
var d3_adderTemp = new d3_adder(); | |
function d3_adderSum(a, b, o) { | |
var x = o.s = a + b, bv = x - a, av = x - bv; | |
o.t = a - av + (b - bv); | |
} | |
d3.geo.stream = function(object, listener) { | |
if (object && d3_geo_streamObjectType.hasOwnProperty(object.type)) { | |
d3_geo_streamObjectType[object.type](object, listener); | |
} else { | |
d3_geo_streamGeometry(object, listener); | |
} | |
}; | |
function d3_geo_streamGeometry(geometry, listener) { | |
if (geometry && d3_geo_streamGeometryType.hasOwnProperty(geometry.type)) { | |
d3_geo_streamGeometryType[geometry.type](geometry, listener); | |
} | |
} | |
var d3_geo_streamObjectType = { | |
Feature: function(feature, listener) { | |
d3_geo_streamGeometry(feature.geometry, listener); | |
}, | |
FeatureCollection: function(object, listener) { | |
var features = object.features, i = -1, n = features.length; | |
while (++i < n) d3_geo_streamGeometry(features[i].geometry, listener); | |
} | |
}; | |
var d3_geo_streamGeometryType = { | |
Sphere: function(object, listener) { | |
listener.sphere(); | |
}, | |
Point: function(object, listener) { | |
object = object.coordinates; | |
listener.point(object[0], object[1], object[2]); | |
}, | |
MultiPoint: function(object, listener) { | |
var coordinates = object.coordinates, i = -1, n = coordinates.length; | |
while (++i < n) object = coordinates[i], listener.point(object[0], object[1], object[2]); | |
}, | |
LineString: function(object, listener) { | |
d3_geo_streamLine(object.coordinates, listener, 0); | |
}, | |
MultiLineString: function(object, listener) { | |
var coordinates = object.coordinates, i = -1, n = coordinates.length; | |
while (++i < n) d3_geo_streamLine(coordinates[i], listener, 0); | |
}, | |
Polygon: function(object, listener) { | |
d3_geo_streamPolygon(object.coordinates, listener); | |
}, | |
MultiPolygon: function(object, listener) { | |
var coordinates = object.coordinates, i = -1, n = coordinates.length; | |
while (++i < n) d3_geo_streamPolygon(coordinates[i], listener); | |
}, | |
GeometryCollection: function(object, listener) { | |
var geometries = object.geometries, i = -1, n = geometries.length; | |
while (++i < n) d3_geo_streamGeometry(geometries[i], listener); | |
} | |
}; | |
function d3_geo_streamLine(coordinates, listener, closed) { | |
var i = -1, n = coordinates.length - closed, coordinate; | |
listener.lineStart(); | |
while (++i < n) coordinate = coordinates[i], listener.point(coordinate[0], coordinate[1], coordinate[2]); | |
listener.lineEnd(); | |
} | |
function d3_geo_streamPolygon(coordinates, listener) { | |
var i = -1, n = coordinates.length; | |
listener.polygonStart(); | |
while (++i < n) d3_geo_streamLine(coordinates[i], listener, 1); | |
listener.polygonEnd(); | |
} | |
d3.geo.area = function(object) { | |
d3_geo_areaSum = 0; | |
d3.geo.stream(object, d3_geo_area); | |
return d3_geo_areaSum; | |
}; | |
var d3_geo_areaSum, d3_geo_areaRingSum = new d3_adder(); | |
var d3_geo_area = { | |
sphere: function() { | |
d3_geo_areaSum += 4 * π; | |
}, | |
point: d3_noop, | |
lineStart: d3_noop, | |
lineEnd: d3_noop, | |
polygonStart: function() { | |
d3_geo_areaRingSum.reset(); | |
d3_geo_area.lineStart = d3_geo_areaRingStart; | |
}, | |
polygonEnd: function() { | |
var area = 2 * d3_geo_areaRingSum; | |
d3_geo_areaSum += area < 0 ? 4 * π + area : area; | |
d3_geo_area.lineStart = d3_geo_area.lineEnd = d3_geo_area.point = d3_noop; | |
} | |
}; | |
function d3_geo_areaRingStart() { | |
var λ00, φ00, λ0, cosφ0, sinφ0; | |
d3_geo_area.point = function(λ, φ) { | |
d3_geo_area.point = nextPoint; | |
λ0 = (λ00 = λ) * d3_radians, cosφ0 = Math.cos(φ = (φ00 = φ) * d3_radians / 2 + π / 4), | |
sinφ0 = Math.sin(φ); | |
}; | |
function nextPoint(λ, φ) { | |
λ *= d3_radians; | |
φ = φ * d3_radians / 2 + π / 4; | |
var dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, cosφ = Math.cos(φ), sinφ = Math.sin(φ), k = sinφ0 * sinφ, u = cosφ0 * cosφ + k * Math.cos(adλ), v = k * sdλ * Math.sin(adλ); | |
d3_geo_areaRingSum.add(Math.atan2(v, u)); | |
λ0 = λ, cosφ0 = cosφ, sinφ0 = sinφ; | |
} | |
d3_geo_area.lineEnd = function() { | |
nextPoint(λ00, φ00); | |
}; | |
} | |
function d3_geo_cartesian(spherical) { | |
var λ = spherical[0], φ = spherical[1], cosφ = Math.cos(φ); | |
return [ cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ) ]; | |
} | |
function d3_geo_cartesianDot(a, b) { | |
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]; | |
} | |
function d3_geo_cartesianCross(a, b) { | |
return [ a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0] ]; | |
} | |
function d3_geo_cartesianAdd(a, b) { | |
a[0] += b[0]; | |
a[1] += b[1]; | |
a[2] += b[2]; | |
} | |
function d3_geo_cartesianScale(vector, k) { | |
return [ vector[0] * k, vector[1] * k, vector[2] * k ]; | |
} | |
function d3_geo_cartesianNormalize(d) { | |
var l = Math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); | |
d[0] /= l; | |
d[1] /= l; | |
d[2] /= l; | |
} | |
function d3_geo_spherical(cartesian) { | |
return [ Math.atan2(cartesian[1], cartesian[0]), d3_asin(cartesian[2]) ]; | |
} | |
function d3_geo_sphericalEqual(a, b) { | |
return abs(a[0] - b[0]) < ε && abs(a[1] - b[1]) < ε; | |
} | |
d3.geo.bounds = function() { | |
var λ0, φ0, λ1, φ1, λ_, λ__, φ__, p0, dλSum, ranges, range; | |
var bound = { | |
point: point, | |
lineStart: lineStart, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
bound.point = ringPoint; | |
bound.lineStart = ringStart; | |
bound.lineEnd = ringEnd; | |
dλSum = 0; | |
d3_geo_area.polygonStart(); | |
}, | |
polygonEnd: function() { | |
d3_geo_area.polygonEnd(); | |
bound.point = point; | |
bound.lineStart = lineStart; | |
bound.lineEnd = lineEnd; | |
if (d3_geo_areaRingSum < 0) λ0 = -(λ1 = 180), φ0 = -(φ1 = 90); else if (dλSum > ε) φ1 = 90; else if (dλSum < -ε) φ0 = -90; | |
range[0] = λ0, range[1] = λ1; | |
} | |
}; | |
function point(λ, φ) { | |
ranges.push(range = [ λ0 = λ, λ1 = λ ]); | |
if (φ < φ0) φ0 = φ; | |
if (φ > φ1) φ1 = φ; | |
} | |
function linePoint(λ, φ) { | |
var p = d3_geo_cartesian([ λ * d3_radians, φ * d3_radians ]); | |
if (p0) { | |
var normal = d3_geo_cartesianCross(p0, p), equatorial = [ normal[1], -normal[0], 0 ], inflection = d3_geo_cartesianCross(equatorial, normal); | |
d3_geo_cartesianNormalize(inflection); | |
inflection = d3_geo_spherical(inflection); | |
var dλ = λ - λ_, s = dλ > 0 ? 1 : -1, λi = inflection[0] * d3_degrees * s, antimeridian = abs(dλ) > 180; | |
if (antimeridian ^ (s * λ_ < λi && λi < s * λ)) { | |
var φi = inflection[1] * d3_degrees; | |
if (φi > φ1) φ1 = φi; | |
} else if (λi = (λi + 360) % 360 - 180, antimeridian ^ (s * λ_ < λi && λi < s * λ)) { | |
var φi = -inflection[1] * d3_degrees; | |
if (φi < φ0) φ0 = φi; | |
} else { | |
if (φ < φ0) φ0 = φ; | |
if (φ > φ1) φ1 = φ; | |
} | |
if (antimeridian) { | |
if (λ < λ_) { | |
if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; | |
} else { | |
if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; | |
} | |
} else { | |
if (λ1 >= λ0) { | |
if (λ < λ0) λ0 = λ; | |
if (λ > λ1) λ1 = λ; | |
} else { | |
if (λ > λ_) { | |
if (angle(λ0, λ) > angle(λ0, λ1)) λ1 = λ; | |
} else { | |
if (angle(λ, λ1) > angle(λ0, λ1)) λ0 = λ; | |
} | |
} | |
} | |
} else { | |
point(λ, φ); | |
} | |
p0 = p, λ_ = λ; | |
} | |
function lineStart() { | |
bound.point = linePoint; | |
} | |
function lineEnd() { | |
range[0] = λ0, range[1] = λ1; | |
bound.point = point; | |
p0 = null; | |
} | |
function ringPoint(λ, φ) { | |
if (p0) { | |
var dλ = λ - λ_; | |
dλSum += abs(dλ) > 180 ? dλ + (dλ > 0 ? 360 : -360) : dλ; | |
} else λ__ = λ, φ__ = φ; | |
d3_geo_area.point(λ, φ); | |
linePoint(λ, φ); | |
} | |
function ringStart() { | |
d3_geo_area.lineStart(); | |
} | |
function ringEnd() { | |
ringPoint(λ__, φ__); | |
d3_geo_area.lineEnd(); | |
if (abs(dλSum) > ε) λ0 = -(λ1 = 180); | |
range[0] = λ0, range[1] = λ1; | |
p0 = null; | |
} | |
function angle(λ0, λ1) { | |
return (λ1 -= λ0) < 0 ? λ1 + 360 : λ1; | |
} | |
function compareRanges(a, b) { | |
return a[0] - b[0]; | |
} | |
function withinRange(x, range) { | |
return range[0] <= range[1] ? range[0] <= x && x <= range[1] : x < range[0] || range[1] < x; | |
} | |
return function(feature) { | |
φ1 = λ1 = -(λ0 = φ0 = Infinity); | |
ranges = []; | |
d3.geo.stream(feature, bound); | |
var n = ranges.length; | |
if (n) { | |
ranges.sort(compareRanges); | |
for (var i = 1, a = ranges[0], b, merged = [ a ]; i < n; ++i) { | |
b = ranges[i]; | |
if (withinRange(b[0], a) || withinRange(b[1], a)) { | |
if (angle(a[0], b[1]) > angle(a[0], a[1])) a[1] = b[1]; | |
if (angle(b[0], a[1]) > angle(a[0], a[1])) a[0] = b[0]; | |
} else { | |
merged.push(a = b); | |
} | |
} | |
var best = -Infinity, dλ; | |
for (var n = merged.length - 1, i = 0, a = merged[n], b; i <= n; a = b, ++i) { | |
b = merged[i]; | |
if ((dλ = angle(a[1], b[0])) > best) best = dλ, λ0 = b[0], λ1 = a[1]; | |
} | |
} | |
ranges = range = null; | |
return λ0 === Infinity || φ0 === Infinity ? [ [ NaN, NaN ], [ NaN, NaN ] ] : [ [ λ0, φ0 ], [ λ1, φ1 ] ]; | |
}; | |
}(); | |
d3.geo.centroid = function(object) { | |
d3_geo_centroidW0 = d3_geo_centroidW1 = d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; | |
d3.geo.stream(object, d3_geo_centroid); | |
var x = d3_geo_centroidX2, y = d3_geo_centroidY2, z = d3_geo_centroidZ2, m = x * x + y * y + z * z; | |
if (m < ε2) { | |
x = d3_geo_centroidX1, y = d3_geo_centroidY1, z = d3_geo_centroidZ1; | |
if (d3_geo_centroidW1 < ε) x = d3_geo_centroidX0, y = d3_geo_centroidY0, z = d3_geo_centroidZ0; | |
m = x * x + y * y + z * z; | |
if (m < ε2) return [ NaN, NaN ]; | |
} | |
return [ Math.atan2(y, x) * d3_degrees, d3_asin(z / Math.sqrt(m)) * d3_degrees ]; | |
}; | |
var d3_geo_centroidW0, d3_geo_centroidW1, d3_geo_centroidX0, d3_geo_centroidY0, d3_geo_centroidZ0, d3_geo_centroidX1, d3_geo_centroidY1, d3_geo_centroidZ1, d3_geo_centroidX2, d3_geo_centroidY2, d3_geo_centroidZ2; | |
var d3_geo_centroid = { | |
sphere: d3_noop, | |
point: d3_geo_centroidPoint, | |
lineStart: d3_geo_centroidLineStart, | |
lineEnd: d3_geo_centroidLineEnd, | |
polygonStart: function() { | |
d3_geo_centroid.lineStart = d3_geo_centroidRingStart; | |
}, | |
polygonEnd: function() { | |
d3_geo_centroid.lineStart = d3_geo_centroidLineStart; | |
} | |
}; | |
function d3_geo_centroidPoint(λ, φ) { | |
λ *= d3_radians; | |
var cosφ = Math.cos(φ *= d3_radians); | |
d3_geo_centroidPointXYZ(cosφ * Math.cos(λ), cosφ * Math.sin(λ), Math.sin(φ)); | |
} | |
function d3_geo_centroidPointXYZ(x, y, z) { | |
++d3_geo_centroidW0; | |
d3_geo_centroidX0 += (x - d3_geo_centroidX0) / d3_geo_centroidW0; | |
d3_geo_centroidY0 += (y - d3_geo_centroidY0) / d3_geo_centroidW0; | |
d3_geo_centroidZ0 += (z - d3_geo_centroidZ0) / d3_geo_centroidW0; | |
} | |
function d3_geo_centroidLineStart() { | |
var x0, y0, z0; | |
d3_geo_centroid.point = function(λ, φ) { | |
λ *= d3_radians; | |
var cosφ = Math.cos(φ *= d3_radians); | |
x0 = cosφ * Math.cos(λ); | |
y0 = cosφ * Math.sin(λ); | |
z0 = Math.sin(φ); | |
d3_geo_centroid.point = nextPoint; | |
d3_geo_centroidPointXYZ(x0, y0, z0); | |
}; | |
function nextPoint(λ, φ) { | |
λ *= d3_radians; | |
var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), w = Math.atan2(Math.sqrt((w = y0 * z - z0 * y) * w + (w = z0 * x - x0 * z) * w + (w = x0 * y - y0 * x) * w), x0 * x + y0 * y + z0 * z); | |
d3_geo_centroidW1 += w; | |
d3_geo_centroidX1 += w * (x0 + (x0 = x)); | |
d3_geo_centroidY1 += w * (y0 + (y0 = y)); | |
d3_geo_centroidZ1 += w * (z0 + (z0 = z)); | |
d3_geo_centroidPointXYZ(x0, y0, z0); | |
} | |
} | |
function d3_geo_centroidLineEnd() { | |
d3_geo_centroid.point = d3_geo_centroidPoint; | |
} | |
function d3_geo_centroidRingStart() { | |
var λ00, φ00, x0, y0, z0; | |
d3_geo_centroid.point = function(λ, φ) { | |
λ00 = λ, φ00 = φ; | |
d3_geo_centroid.point = nextPoint; | |
λ *= d3_radians; | |
var cosφ = Math.cos(φ *= d3_radians); | |
x0 = cosφ * Math.cos(λ); | |
y0 = cosφ * Math.sin(λ); | |
z0 = Math.sin(φ); | |
d3_geo_centroidPointXYZ(x0, y0, z0); | |
}; | |
d3_geo_centroid.lineEnd = function() { | |
nextPoint(λ00, φ00); | |
d3_geo_centroid.lineEnd = d3_geo_centroidLineEnd; | |
d3_geo_centroid.point = d3_geo_centroidPoint; | |
}; | |
function nextPoint(λ, φ) { | |
λ *= d3_radians; | |
var cosφ = Math.cos(φ *= d3_radians), x = cosφ * Math.cos(λ), y = cosφ * Math.sin(λ), z = Math.sin(φ), cx = y0 * z - z0 * y, cy = z0 * x - x0 * z, cz = x0 * y - y0 * x, m = Math.sqrt(cx * cx + cy * cy + cz * cz), u = x0 * x + y0 * y + z0 * z, v = m && -d3_acos(u) / m, w = Math.atan2(m, u); | |
d3_geo_centroidX2 += v * cx; | |
d3_geo_centroidY2 += v * cy; | |
d3_geo_centroidZ2 += v * cz; | |
d3_geo_centroidW1 += w; | |
d3_geo_centroidX1 += w * (x0 + (x0 = x)); | |
d3_geo_centroidY1 += w * (y0 + (y0 = y)); | |
d3_geo_centroidZ1 += w * (z0 + (z0 = z)); | |
d3_geo_centroidPointXYZ(x0, y0, z0); | |
} | |
} | |
function d3_geo_compose(a, b) { | |
function compose(x, y) { | |
return x = a(x, y), b(x[0], x[1]); | |
} | |
if (a.invert && b.invert) compose.invert = function(x, y) { | |
return x = b.invert(x, y), x && a.invert(x[0], x[1]); | |
}; | |
return compose; | |
} | |
function d3_true() { | |
return true; | |
} | |
function d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener) { | |
var subject = [], clip = []; | |
segments.forEach(function(segment) { | |
if ((n = segment.length - 1) <= 0) return; | |
var n, p0 = segment[0], p1 = segment[n]; | |
if (d3_geo_sphericalEqual(p0, p1)) { | |
listener.lineStart(); | |
for (var i = 0; i < n; ++i) listener.point((p0 = segment[i])[0], p0[1]); | |
listener.lineEnd(); | |
return; | |
} | |
var a = new d3_geo_clipPolygonIntersection(p0, segment, null, true), b = new d3_geo_clipPolygonIntersection(p0, null, a, false); | |
a.o = b; | |
subject.push(a); | |
clip.push(b); | |
a = new d3_geo_clipPolygonIntersection(p1, segment, null, false); | |
b = new d3_geo_clipPolygonIntersection(p1, null, a, true); | |
a.o = b; | |
subject.push(a); | |
clip.push(b); | |
}); | |
clip.sort(compare); | |
d3_geo_clipPolygonLinkCircular(subject); | |
d3_geo_clipPolygonLinkCircular(clip); | |
if (!subject.length) return; | |
for (var i = 0, entry = clipStartInside, n = clip.length; i < n; ++i) { | |
clip[i].e = entry = !entry; | |
} | |
var start = subject[0], points, point; | |
while (1) { | |
var current = start, isSubject = true; | |
while (current.v) if ((current = current.n) === start) return; | |
points = current.z; | |
listener.lineStart(); | |
do { | |
current.v = current.o.v = true; | |
if (current.e) { | |
if (isSubject) { | |
for (var i = 0, n = points.length; i < n; ++i) listener.point((point = points[i])[0], point[1]); | |
} else { | |
interpolate(current.x, current.n.x, 1, listener); | |
} | |
current = current.n; | |
} else { | |
if (isSubject) { | |
points = current.p.z; | |
for (var i = points.length - 1; i >= 0; --i) listener.point((point = points[i])[0], point[1]); | |
} else { | |
interpolate(current.x, current.p.x, -1, listener); | |
} | |
current = current.p; | |
} | |
current = current.o; | |
points = current.z; | |
isSubject = !isSubject; | |
} while (!current.v); | |
listener.lineEnd(); | |
} | |
} | |
function d3_geo_clipPolygonLinkCircular(array) { | |
if (!(n = array.length)) return; | |
var n, i = 0, a = array[0], b; | |
while (++i < n) { | |
a.n = b = array[i]; | |
b.p = a; | |
a = b; | |
} | |
a.n = b = array[0]; | |
b.p = a; | |
} | |
function d3_geo_clipPolygonIntersection(point, points, other, entry) { | |
this.x = point; | |
this.z = points; | |
this.o = other; | |
this.e = entry; | |
this.v = false; | |
this.n = this.p = null; | |
} | |
function d3_geo_clip(pointVisible, clipLine, interpolate, clipStart) { | |
return function(rotate, listener) { | |
var line = clipLine(listener), rotatedClipStart = rotate.invert(clipStart[0], clipStart[1]); | |
var clip = { | |
point: point, | |
lineStart: lineStart, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
clip.point = pointRing; | |
clip.lineStart = ringStart; | |
clip.lineEnd = ringEnd; | |
segments = []; | |
polygon = []; | |
}, | |
polygonEnd: function() { | |
clip.point = point; | |
clip.lineStart = lineStart; | |
clip.lineEnd = lineEnd; | |
segments = d3.merge(segments); | |
var clipStartInside = d3_geo_pointInPolygon(rotatedClipStart, polygon); | |
if (segments.length) { | |
if (!polygonStarted) listener.polygonStart(), polygonStarted = true; | |
d3_geo_clipPolygon(segments, d3_geo_clipSort, clipStartInside, interpolate, listener); | |
} else if (clipStartInside) { | |
if (!polygonStarted) listener.polygonStart(), polygonStarted = true; | |
listener.lineStart(); | |
interpolate(null, null, 1, listener); | |
listener.lineEnd(); | |
} | |
if (polygonStarted) listener.polygonEnd(), polygonStarted = false; | |
segments = polygon = null; | |
}, | |
sphere: function() { | |
listener.polygonStart(); | |
listener.lineStart(); | |
interpolate(null, null, 1, listener); | |
listener.lineEnd(); | |
listener.polygonEnd(); | |
} | |
}; | |
function point(λ, φ) { | |
var point = rotate(λ, φ); | |
if (pointVisible(λ = point[0], φ = point[1])) listener.point(λ, φ); | |
} | |
function pointLine(λ, φ) { | |
var point = rotate(λ, φ); | |
line.point(point[0], point[1]); | |
} | |
function lineStart() { | |
clip.point = pointLine; | |
line.lineStart(); | |
} | |
function lineEnd() { | |
clip.point = point; | |
line.lineEnd(); | |
} | |
var segments; | |
var buffer = d3_geo_clipBufferListener(), ringListener = clipLine(buffer), polygonStarted = false, polygon, ring; | |
function pointRing(λ, φ) { | |
ring.push([ λ, φ ]); | |
var point = rotate(λ, φ); | |
ringListener.point(point[0], point[1]); | |
} | |
function ringStart() { | |
ringListener.lineStart(); | |
ring = []; | |
} | |
function ringEnd() { | |
pointRing(ring[0][0], ring[0][1]); | |
ringListener.lineEnd(); | |
var clean = ringListener.clean(), ringSegments = buffer.buffer(), segment, n = ringSegments.length; | |
ring.pop(); | |
polygon.push(ring); | |
ring = null; | |
if (!n) return; | |
if (clean & 1) { | |
segment = ringSegments[0]; | |
var n = segment.length - 1, i = -1, point; | |
if (n > 0) { | |
if (!polygonStarted) listener.polygonStart(), polygonStarted = true; | |
listener.lineStart(); | |
while (++i < n) listener.point((point = segment[i])[0], point[1]); | |
listener.lineEnd(); | |
} | |
return; | |
} | |
if (n > 1 && clean & 2) ringSegments.push(ringSegments.pop().concat(ringSegments.shift())); | |
segments.push(ringSegments.filter(d3_geo_clipSegmentLength1)); | |
} | |
return clip; | |
}; | |
} | |
function d3_geo_clipSegmentLength1(segment) { | |
return segment.length > 1; | |
} | |
function d3_geo_clipBufferListener() { | |
var lines = [], line; | |
return { | |
lineStart: function() { | |
lines.push(line = []); | |
}, | |
point: function(λ, φ) { | |
line.push([ λ, φ ]); | |
}, | |
lineEnd: d3_noop, | |
buffer: function() { | |
var buffer = lines; | |
lines = []; | |
line = null; | |
return buffer; | |
}, | |
rejoin: function() { | |
if (lines.length > 1) lines.push(lines.pop().concat(lines.shift())); | |
} | |
}; | |
} | |
function d3_geo_clipSort(a, b) { | |
return ((a = a.x)[0] < 0 ? a[1] - halfπ - ε : halfπ - a[1]) - ((b = b.x)[0] < 0 ? b[1] - halfπ - ε : halfπ - b[1]); | |
} | |
var d3_geo_clipAntimeridian = d3_geo_clip(d3_true, d3_geo_clipAntimeridianLine, d3_geo_clipAntimeridianInterpolate, [ -π, -π / 2 ]); | |
function d3_geo_clipAntimeridianLine(listener) { | |
var λ0 = NaN, φ0 = NaN, sλ0 = NaN, clean; | |
return { | |
lineStart: function() { | |
listener.lineStart(); | |
clean = 1; | |
}, | |
point: function(λ1, φ1) { | |
var sλ1 = λ1 > 0 ? π : -π, dλ = abs(λ1 - λ0); | |
if (abs(dλ - π) < ε) { | |
listener.point(λ0, φ0 = (φ0 + φ1) / 2 > 0 ? halfπ : -halfπ); | |
listener.point(sλ0, φ0); | |
listener.lineEnd(); | |
listener.lineStart(); | |
listener.point(sλ1, φ0); | |
listener.point(λ1, φ0); | |
clean = 0; | |
} else if (sλ0 !== sλ1 && dλ >= π) { | |
if (abs(λ0 - sλ0) < ε) λ0 -= sλ0 * ε; | |
if (abs(λ1 - sλ1) < ε) λ1 -= sλ1 * ε; | |
φ0 = d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1); | |
listener.point(sλ0, φ0); | |
listener.lineEnd(); | |
listener.lineStart(); | |
listener.point(sλ1, φ0); | |
clean = 0; | |
} | |
listener.point(λ0 = λ1, φ0 = φ1); | |
sλ0 = sλ1; | |
}, | |
lineEnd: function() { | |
listener.lineEnd(); | |
λ0 = φ0 = NaN; | |
}, | |
clean: function() { | |
return 2 - clean; | |
} | |
}; | |
} | |
function d3_geo_clipAntimeridianIntersect(λ0, φ0, λ1, φ1) { | |
var cosφ0, cosφ1, sinλ0_λ1 = Math.sin(λ0 - λ1); | |
return abs(sinλ0_λ1) > ε ? Math.atan((Math.sin(φ0) * (cosφ1 = Math.cos(φ1)) * Math.sin(λ1) - Math.sin(φ1) * (cosφ0 = Math.cos(φ0)) * Math.sin(λ0)) / (cosφ0 * cosφ1 * sinλ0_λ1)) : (φ0 + φ1) / 2; | |
} | |
function d3_geo_clipAntimeridianInterpolate(from, to, direction, listener) { | |
var φ; | |
if (from == null) { | |
φ = direction * halfπ; | |
listener.point(-π, φ); | |
listener.point(0, φ); | |
listener.point(π, φ); | |
listener.point(π, 0); | |
listener.point(π, -φ); | |
listener.point(0, -φ); | |
listener.point(-π, -φ); | |
listener.point(-π, 0); | |
listener.point(-π, φ); | |
} else if (abs(from[0] - to[0]) > ε) { | |
var s = from[0] < to[0] ? π : -π; | |
φ = direction * s / 2; | |
listener.point(-s, φ); | |
listener.point(0, φ); | |
listener.point(s, φ); | |
} else { | |
listener.point(to[0], to[1]); | |
} | |
} | |
function d3_geo_pointInPolygon(point, polygon) { | |
var meridian = point[0], parallel = point[1], meridianNormal = [ Math.sin(meridian), -Math.cos(meridian), 0 ], polarAngle = 0, winding = 0; | |
d3_geo_areaRingSum.reset(); | |
for (var i = 0, n = polygon.length; i < n; ++i) { | |
var ring = polygon[i], m = ring.length; | |
if (!m) continue; | |
var point0 = ring[0], λ0 = point0[0], φ0 = point0[1] / 2 + π / 4, sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), j = 1; | |
while (true) { | |
if (j === m) j = 0; | |
point = ring[j]; | |
var λ = point[0], φ = point[1] / 2 + π / 4, sinφ = Math.sin(φ), cosφ = Math.cos(φ), dλ = λ - λ0, sdλ = dλ >= 0 ? 1 : -1, adλ = sdλ * dλ, antimeridian = adλ > π, k = sinφ0 * sinφ; | |
d3_geo_areaRingSum.add(Math.atan2(k * sdλ * Math.sin(adλ), cosφ0 * cosφ + k * Math.cos(adλ))); | |
polarAngle += antimeridian ? dλ + sdλ * τ : dλ; | |
if (antimeridian ^ λ0 >= meridian ^ λ >= meridian) { | |
var arc = d3_geo_cartesianCross(d3_geo_cartesian(point0), d3_geo_cartesian(point)); | |
d3_geo_cartesianNormalize(arc); | |
var intersection = d3_geo_cartesianCross(meridianNormal, arc); | |
d3_geo_cartesianNormalize(intersection); | |
var φarc = (antimeridian ^ dλ >= 0 ? -1 : 1) * d3_asin(intersection[2]); | |
if (parallel > φarc || parallel === φarc && (arc[0] || arc[1])) { | |
winding += antimeridian ^ dλ >= 0 ? 1 : -1; | |
} | |
} | |
if (!j++) break; | |
λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ, point0 = point; | |
} | |
} | |
return (polarAngle < -ε || polarAngle < ε && d3_geo_areaRingSum < 0) ^ winding & 1; | |
} | |
function d3_geo_clipCircle(radius) { | |
var cr = Math.cos(radius), smallRadius = cr > 0, notHemisphere = abs(cr) > ε, interpolate = d3_geo_circleInterpolate(radius, 6 * d3_radians); | |
return d3_geo_clip(visible, clipLine, interpolate, smallRadius ? [ 0, -radius ] : [ -π, radius - π ]); | |
function visible(λ, φ) { | |
return Math.cos(λ) * Math.cos(φ) > cr; | |
} | |
function clipLine(listener) { | |
var point0, c0, v0, v00, clean; | |
return { | |
lineStart: function() { | |
v00 = v0 = false; | |
clean = 1; | |
}, | |
point: function(λ, φ) { | |
var point1 = [ λ, φ ], point2, v = visible(λ, φ), c = smallRadius ? v ? 0 : code(λ, φ) : v ? code(λ + (λ < 0 ? π : -π), φ) : 0; | |
if (!point0 && (v00 = v0 = v)) listener.lineStart(); | |
if (v !== v0) { | |
point2 = intersect(point0, point1); | |
if (d3_geo_sphericalEqual(point0, point2) || d3_geo_sphericalEqual(point1, point2)) { | |
point1[0] += ε; | |
point1[1] += ε; | |
v = visible(point1[0], point1[1]); | |
} | |
} | |
if (v !== v0) { | |
clean = 0; | |
if (v) { | |
listener.lineStart(); | |
point2 = intersect(point1, point0); | |
listener.point(point2[0], point2[1]); | |
} else { | |
point2 = intersect(point0, point1); | |
listener.point(point2[0], point2[1]); | |
listener.lineEnd(); | |
} | |
point0 = point2; | |
} else if (notHemisphere && point0 && smallRadius ^ v) { | |
var t; | |
if (!(c & c0) && (t = intersect(point1, point0, true))) { | |
clean = 0; | |
if (smallRadius) { | |
listener.lineStart(); | |
listener.point(t[0][0], t[0][1]); | |
listener.point(t[1][0], t[1][1]); | |
listener.lineEnd(); | |
} else { | |
listener.point(t[1][0], t[1][1]); | |
listener.lineEnd(); | |
listener.lineStart(); | |
listener.point(t[0][0], t[0][1]); | |
} | |
} | |
} | |
if (v && (!point0 || !d3_geo_sphericalEqual(point0, point1))) { | |
listener.point(point1[0], point1[1]); | |
} | |
point0 = point1, v0 = v, c0 = c; | |
}, | |
lineEnd: function() { | |
if (v0) listener.lineEnd(); | |
point0 = null; | |
}, | |
clean: function() { | |
return clean | (v00 && v0) << 1; | |
} | |
}; | |
} | |
function intersect(a, b, two) { | |
var pa = d3_geo_cartesian(a), pb = d3_geo_cartesian(b); | |
var n1 = [ 1, 0, 0 ], n2 = d3_geo_cartesianCross(pa, pb), n2n2 = d3_geo_cartesianDot(n2, n2), n1n2 = n2[0], determinant = n2n2 - n1n2 * n1n2; | |
if (!determinant) return !two && a; | |
var c1 = cr * n2n2 / determinant, c2 = -cr * n1n2 / determinant, n1xn2 = d3_geo_cartesianCross(n1, n2), A = d3_geo_cartesianScale(n1, c1), B = d3_geo_cartesianScale(n2, c2); | |
d3_geo_cartesianAdd(A, B); | |
var u = n1xn2, w = d3_geo_cartesianDot(A, u), uu = d3_geo_cartesianDot(u, u), t2 = w * w - uu * (d3_geo_cartesianDot(A, A) - 1); | |
if (t2 < 0) return; | |
var t = Math.sqrt(t2), q = d3_geo_cartesianScale(u, (-w - t) / uu); | |
d3_geo_cartesianAdd(q, A); | |
q = d3_geo_spherical(q); | |
if (!two) return q; | |
var λ0 = a[0], λ1 = b[0], φ0 = a[1], φ1 = b[1], z; | |
if (λ1 < λ0) z = λ0, λ0 = λ1, λ1 = z; | |
var δλ = λ1 - λ0, polar = abs(δλ - π) < ε, meridian = polar || δλ < ε; | |
if (!polar && φ1 < φ0) z = φ0, φ0 = φ1, φ1 = z; | |
if (meridian ? polar ? φ0 + φ1 > 0 ^ q[1] < (abs(q[0] - λ0) < ε ? φ0 : φ1) : φ0 <= q[1] && q[1] <= φ1 : δλ > π ^ (λ0 <= q[0] && q[0] <= λ1)) { | |
var q1 = d3_geo_cartesianScale(u, (-w + t) / uu); | |
d3_geo_cartesianAdd(q1, A); | |
return [ q, d3_geo_spherical(q1) ]; | |
} | |
} | |
function code(λ, φ) { | |
var r = smallRadius ? radius : π - radius, code = 0; | |
if (λ < -r) code |= 1; else if (λ > r) code |= 2; | |
if (φ < -r) code |= 4; else if (φ > r) code |= 8; | |
return code; | |
} | |
} | |
function d3_geom_clipLine(x0, y0, x1, y1) { | |
return function(line) { | |
var a = line.a, b = line.b, ax = a.x, ay = a.y, bx = b.x, by = b.y, t0 = 0, t1 = 1, dx = bx - ax, dy = by - ay, r; | |
r = x0 - ax; | |
if (!dx && r > 0) return; | |
r /= dx; | |
if (dx < 0) { | |
if (r < t0) return; | |
if (r < t1) t1 = r; | |
} else if (dx > 0) { | |
if (r > t1) return; | |
if (r > t0) t0 = r; | |
} | |
r = x1 - ax; | |
if (!dx && r < 0) return; | |
r /= dx; | |
if (dx < 0) { | |
if (r > t1) return; | |
if (r > t0) t0 = r; | |
} else if (dx > 0) { | |
if (r < t0) return; | |
if (r < t1) t1 = r; | |
} | |
r = y0 - ay; | |
if (!dy && r > 0) return; | |
r /= dy; | |
if (dy < 0) { | |
if (r < t0) return; | |
if (r < t1) t1 = r; | |
} else if (dy > 0) { | |
if (r > t1) return; | |
if (r > t0) t0 = r; | |
} | |
r = y1 - ay; | |
if (!dy && r < 0) return; | |
r /= dy; | |
if (dy < 0) { | |
if (r > t1) return; | |
if (r > t0) t0 = r; | |
} else if (dy > 0) { | |
if (r < t0) return; | |
if (r < t1) t1 = r; | |
} | |
if (t0 > 0) line.a = { | |
x: ax + t0 * dx, | |
y: ay + t0 * dy | |
}; | |
if (t1 < 1) line.b = { | |
x: ax + t1 * dx, | |
y: ay + t1 * dy | |
}; | |
return line; | |
}; | |
} | |
var d3_geo_clipExtentMAX = 1e9; | |
d3.geo.clipExtent = function() { | |
var x0, y0, x1, y1, stream, clip, clipExtent = { | |
stream: function(output) { | |
if (stream) stream.valid = false; | |
stream = clip(output); | |
stream.valid = true; | |
return stream; | |
}, | |
extent: function(_) { | |
if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; | |
clip = d3_geo_clipExtent(x0 = +_[0][0], y0 = +_[0][1], x1 = +_[1][0], y1 = +_[1][1]); | |
if (stream) stream.valid = false, stream = null; | |
return clipExtent; | |
} | |
}; | |
return clipExtent.extent([ [ 0, 0 ], [ 960, 500 ] ]); | |
}; | |
function d3_geo_clipExtent(x0, y0, x1, y1) { | |
return function(listener) { | |
var listener_ = listener, bufferListener = d3_geo_clipBufferListener(), clipLine = d3_geom_clipLine(x0, y0, x1, y1), segments, polygon, ring; | |
var clip = { | |
point: point, | |
lineStart: lineStart, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
listener = bufferListener; | |
segments = []; | |
polygon = []; | |
clean = true; | |
}, | |
polygonEnd: function() { | |
listener = listener_; | |
segments = d3.merge(segments); | |
var clipStartInside = insidePolygon([ x0, y1 ]), inside = clean && clipStartInside, visible = segments.length; | |
if (inside || visible) { | |
listener.polygonStart(); | |
if (inside) { | |
listener.lineStart(); | |
interpolate(null, null, 1, listener); | |
listener.lineEnd(); | |
} | |
if (visible) { | |
d3_geo_clipPolygon(segments, compare, clipStartInside, interpolate, listener); | |
} | |
listener.polygonEnd(); | |
} | |
segments = polygon = ring = null; | |
} | |
}; | |
function insidePolygon(p) { | |
var wn = 0, n = polygon.length, y = p[1]; | |
for (var i = 0; i < n; ++i) { | |
for (var j = 1, v = polygon[i], m = v.length, a = v[0], b; j < m; ++j) { | |
b = v[j]; | |
if (a[1] <= y) { | |
if (b[1] > y && d3_cross2d(a, b, p) > 0) ++wn; | |
} else { | |
if (b[1] <= y && d3_cross2d(a, b, p) < 0) --wn; | |
} | |
a = b; | |
} | |
} | |
return wn !== 0; | |
} | |
function interpolate(from, to, direction, listener) { | |
var a = 0, a1 = 0; | |
if (from == null || (a = corner(from, direction)) !== (a1 = corner(to, direction)) || comparePoints(from, to) < 0 ^ direction > 0) { | |
do { | |
listener.point(a === 0 || a === 3 ? x0 : x1, a > 1 ? y1 : y0); | |
} while ((a = (a + direction + 4) % 4) !== a1); | |
} else { | |
listener.point(to[0], to[1]); | |
} | |
} | |
function pointVisible(x, y) { | |
return x0 <= x && x <= x1 && y0 <= y && y <= y1; | |
} | |
function point(x, y) { | |
if (pointVisible(x, y)) listener.point(x, y); | |
} | |
var x__, y__, v__, x_, y_, v_, first, clean; | |
function lineStart() { | |
clip.point = linePoint; | |
if (polygon) polygon.push(ring = []); | |
first = true; | |
v_ = false; | |
x_ = y_ = NaN; | |
} | |
function lineEnd() { | |
if (segments) { | |
linePoint(x__, y__); | |
if (v__ && v_) bufferListener.rejoin(); | |
segments.push(bufferListener.buffer()); | |
} | |
clip.point = point; | |
if (v_) listener.lineEnd(); | |
} | |
function linePoint(x, y) { | |
x = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, x)); | |
y = Math.max(-d3_geo_clipExtentMAX, Math.min(d3_geo_clipExtentMAX, y)); | |
var v = pointVisible(x, y); | |
if (polygon) ring.push([ x, y ]); | |
if (first) { | |
x__ = x, y__ = y, v__ = v; | |
first = false; | |
if (v) { | |
listener.lineStart(); | |
listener.point(x, y); | |
} | |
} else { | |
if (v && v_) listener.point(x, y); else { | |
var l = { | |
a: { | |
x: x_, | |
y: y_ | |
}, | |
b: { | |
x: x, | |
y: y | |
} | |
}; | |
if (clipLine(l)) { | |
if (!v_) { | |
listener.lineStart(); | |
listener.point(l.a.x, l.a.y); | |
} | |
listener.point(l.b.x, l.b.y); | |
if (!v) listener.lineEnd(); | |
clean = false; | |
} else if (v) { | |
listener.lineStart(); | |
listener.point(x, y); | |
clean = false; | |
} | |
} | |
} | |
x_ = x, y_ = y, v_ = v; | |
} | |
return clip; | |
}; | |
function corner(p, direction) { | |
return abs(p[0] - x0) < ε ? direction > 0 ? 0 : 3 : abs(p[0] - x1) < ε ? direction > 0 ? 2 : 1 : abs(p[1] - y0) < ε ? direction > 0 ? 1 : 0 : direction > 0 ? 3 : 2; | |
} | |
function compare(a, b) { | |
return comparePoints(a.x, b.x); | |
} | |
function comparePoints(a, b) { | |
var ca = corner(a, 1), cb = corner(b, 1); | |
return ca !== cb ? ca - cb : ca === 0 ? b[1] - a[1] : ca === 1 ? a[0] - b[0] : ca === 2 ? a[1] - b[1] : b[0] - a[0]; | |
} | |
} | |
function d3_geo_conic(projectAt) { | |
var φ0 = 0, φ1 = π / 3, m = d3_geo_projectionMutator(projectAt), p = m(φ0, φ1); | |
p.parallels = function(_) { | |
if (!arguments.length) return [ φ0 / π * 180, φ1 / π * 180 ]; | |
return m(φ0 = _[0] * π / 180, φ1 = _[1] * π / 180); | |
}; | |
return p; | |
} | |
function d3_geo_conicEqualArea(φ0, φ1) { | |
var sinφ0 = Math.sin(φ0), n = (sinφ0 + Math.sin(φ1)) / 2, C = 1 + sinφ0 * (2 * n - sinφ0), ρ0 = Math.sqrt(C) / n; | |
function forward(λ, φ) { | |
var ρ = Math.sqrt(C - 2 * n * Math.sin(φ)) / n; | |
return [ ρ * Math.sin(λ *= n), ρ0 - ρ * Math.cos(λ) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ0_y = ρ0 - y; | |
return [ Math.atan2(x, ρ0_y) / n, d3_asin((C - (x * x + ρ0_y * ρ0_y) * n * n) / (2 * n)) ]; | |
}; | |
return forward; | |
} | |
(d3.geo.conicEqualArea = function() { | |
return d3_geo_conic(d3_geo_conicEqualArea); | |
}).raw = d3_geo_conicEqualArea; | |
d3.geo.albers = function() { | |
return d3.geo.conicEqualArea().rotate([ 96, 0 ]).center([ -.6, 38.7 ]).parallels([ 29.5, 45.5 ]).scale(1070); | |
}; | |
d3.geo.albersUsa = function() { | |
var lower48 = d3.geo.albers(); | |
var alaska = d3.geo.conicEqualArea().rotate([ 154, 0 ]).center([ -2, 58.5 ]).parallels([ 55, 65 ]); | |
var hawaii = d3.geo.conicEqualArea().rotate([ 157, 0 ]).center([ -3, 19.9 ]).parallels([ 8, 18 ]); | |
var point, pointStream = { | |
point: function(x, y) { | |
point = [ x, y ]; | |
} | |
}, lower48Point, alaskaPoint, hawaiiPoint; | |
function albersUsa(coordinates) { | |
var x = coordinates[0], y = coordinates[1]; | |
point = null; | |
(lower48Point(x, y), point) || (alaskaPoint(x, y), point) || hawaiiPoint(x, y); | |
return point; | |
} | |
albersUsa.invert = function(coordinates) { | |
var k = lower48.scale(), t = lower48.translate(), x = (coordinates[0] - t[0]) / k, y = (coordinates[1] - t[1]) / k; | |
return (y >= .12 && y < .234 && x >= -.425 && x < -.214 ? alaska : y >= .166 && y < .234 && x >= -.214 && x < -.115 ? hawaii : lower48).invert(coordinates); | |
}; | |
albersUsa.stream = function(stream) { | |
var lower48Stream = lower48.stream(stream), alaskaStream = alaska.stream(stream), hawaiiStream = hawaii.stream(stream); | |
return { | |
point: function(x, y) { | |
lower48Stream.point(x, y); | |
alaskaStream.point(x, y); | |
hawaiiStream.point(x, y); | |
}, | |
sphere: function() { | |
lower48Stream.sphere(); | |
alaskaStream.sphere(); | |
hawaiiStream.sphere(); | |
}, | |
lineStart: function() { | |
lower48Stream.lineStart(); | |
alaskaStream.lineStart(); | |
hawaiiStream.lineStart(); | |
}, | |
lineEnd: function() { | |
lower48Stream.lineEnd(); | |
alaskaStream.lineEnd(); | |
hawaiiStream.lineEnd(); | |
}, | |
polygonStart: function() { | |
lower48Stream.polygonStart(); | |
alaskaStream.polygonStart(); | |
hawaiiStream.polygonStart(); | |
}, | |
polygonEnd: function() { | |
lower48Stream.polygonEnd(); | |
alaskaStream.polygonEnd(); | |
hawaiiStream.polygonEnd(); | |
} | |
}; | |
}; | |
albersUsa.precision = function(_) { | |
if (!arguments.length) return lower48.precision(); | |
lower48.precision(_); | |
alaska.precision(_); | |
hawaii.precision(_); | |
return albersUsa; | |
}; | |
albersUsa.scale = function(_) { | |
if (!arguments.length) return lower48.scale(); | |
lower48.scale(_); | |
alaska.scale(_ * .35); | |
hawaii.scale(_); | |
return albersUsa.translate(lower48.translate()); | |
}; | |
albersUsa.translate = function(_) { | |
if (!arguments.length) return lower48.translate(); | |
var k = lower48.scale(), x = +_[0], y = +_[1]; | |
lower48Point = lower48.translate(_).clipExtent([ [ x - .455 * k, y - .238 * k ], [ x + .455 * k, y + .238 * k ] ]).stream(pointStream).point; | |
alaskaPoint = alaska.translate([ x - .307 * k, y + .201 * k ]).clipExtent([ [ x - .425 * k + ε, y + .12 * k + ε ], [ x - .214 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; | |
hawaiiPoint = hawaii.translate([ x - .205 * k, y + .212 * k ]).clipExtent([ [ x - .214 * k + ε, y + .166 * k + ε ], [ x - .115 * k - ε, y + .234 * k - ε ] ]).stream(pointStream).point; | |
return albersUsa; | |
}; | |
return albersUsa.scale(1070); | |
}; | |
var d3_geo_pathAreaSum, d3_geo_pathAreaPolygon, d3_geo_pathArea = { | |
point: d3_noop, | |
lineStart: d3_noop, | |
lineEnd: d3_noop, | |
polygonStart: function() { | |
d3_geo_pathAreaPolygon = 0; | |
d3_geo_pathArea.lineStart = d3_geo_pathAreaRingStart; | |
}, | |
polygonEnd: function() { | |
d3_geo_pathArea.lineStart = d3_geo_pathArea.lineEnd = d3_geo_pathArea.point = d3_noop; | |
d3_geo_pathAreaSum += abs(d3_geo_pathAreaPolygon / 2); | |
} | |
}; | |
function d3_geo_pathAreaRingStart() { | |
var x00, y00, x0, y0; | |
d3_geo_pathArea.point = function(x, y) { | |
d3_geo_pathArea.point = nextPoint; | |
x00 = x0 = x, y00 = y0 = y; | |
}; | |
function nextPoint(x, y) { | |
d3_geo_pathAreaPolygon += y0 * x - x0 * y; | |
x0 = x, y0 = y; | |
} | |
d3_geo_pathArea.lineEnd = function() { | |
nextPoint(x00, y00); | |
}; | |
} | |
var d3_geo_pathBoundsX0, d3_geo_pathBoundsY0, d3_geo_pathBoundsX1, d3_geo_pathBoundsY1; | |
var d3_geo_pathBounds = { | |
point: d3_geo_pathBoundsPoint, | |
lineStart: d3_noop, | |
lineEnd: d3_noop, | |
polygonStart: d3_noop, | |
polygonEnd: d3_noop | |
}; | |
function d3_geo_pathBoundsPoint(x, y) { | |
if (x < d3_geo_pathBoundsX0) d3_geo_pathBoundsX0 = x; | |
if (x > d3_geo_pathBoundsX1) d3_geo_pathBoundsX1 = x; | |
if (y < d3_geo_pathBoundsY0) d3_geo_pathBoundsY0 = y; | |
if (y > d3_geo_pathBoundsY1) d3_geo_pathBoundsY1 = y; | |
} | |
function d3_geo_pathBuffer() { | |
var pointCircle = d3_geo_pathBufferCircle(4.5), buffer = []; | |
var stream = { | |
point: point, | |
lineStart: function() { | |
stream.point = pointLineStart; | |
}, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
stream.lineEnd = lineEndPolygon; | |
}, | |
polygonEnd: function() { | |
stream.lineEnd = lineEnd; | |
stream.point = point; | |
}, | |
pointRadius: function(_) { | |
pointCircle = d3_geo_pathBufferCircle(_); | |
return stream; | |
}, | |
result: function() { | |
if (buffer.length) { | |
var result = buffer.join(""); | |
buffer = []; | |
return result; | |
} | |
} | |
}; | |
function point(x, y) { | |
buffer.push("M", x, ",", y, pointCircle); | |
} | |
function pointLineStart(x, y) { | |
buffer.push("M", x, ",", y); | |
stream.point = pointLine; | |
} | |
function pointLine(x, y) { | |
buffer.push("L", x, ",", y); | |
} | |
function lineEnd() { | |
stream.point = point; | |
} | |
function lineEndPolygon() { | |
buffer.push("Z"); | |
} | |
return stream; | |
} | |
function d3_geo_pathBufferCircle(radius) { | |
return "m0," + radius + "a" + radius + "," + radius + " 0 1,1 0," + -2 * radius + "a" + radius + "," + radius + " 0 1,1 0," + 2 * radius + "z"; | |
} | |
var d3_geo_pathCentroid = { | |
point: d3_geo_pathCentroidPoint, | |
lineStart: d3_geo_pathCentroidLineStart, | |
lineEnd: d3_geo_pathCentroidLineEnd, | |
polygonStart: function() { | |
d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidRingStart; | |
}, | |
polygonEnd: function() { | |
d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; | |
d3_geo_pathCentroid.lineStart = d3_geo_pathCentroidLineStart; | |
d3_geo_pathCentroid.lineEnd = d3_geo_pathCentroidLineEnd; | |
} | |
}; | |
function d3_geo_pathCentroidPoint(x, y) { | |
d3_geo_centroidX0 += x; | |
d3_geo_centroidY0 += y; | |
++d3_geo_centroidZ0; | |
} | |
function d3_geo_pathCentroidLineStart() { | |
var x0, y0; | |
d3_geo_pathCentroid.point = function(x, y) { | |
d3_geo_pathCentroid.point = nextPoint; | |
d3_geo_pathCentroidPoint(x0 = x, y0 = y); | |
}; | |
function nextPoint(x, y) { | |
var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); | |
d3_geo_centroidX1 += z * (x0 + x) / 2; | |
d3_geo_centroidY1 += z * (y0 + y) / 2; | |
d3_geo_centroidZ1 += z; | |
d3_geo_pathCentroidPoint(x0 = x, y0 = y); | |
} | |
} | |
function d3_geo_pathCentroidLineEnd() { | |
d3_geo_pathCentroid.point = d3_geo_pathCentroidPoint; | |
} | |
function d3_geo_pathCentroidRingStart() { | |
var x00, y00, x0, y0; | |
d3_geo_pathCentroid.point = function(x, y) { | |
d3_geo_pathCentroid.point = nextPoint; | |
d3_geo_pathCentroidPoint(x00 = x0 = x, y00 = y0 = y); | |
}; | |
function nextPoint(x, y) { | |
var dx = x - x0, dy = y - y0, z = Math.sqrt(dx * dx + dy * dy); | |
d3_geo_centroidX1 += z * (x0 + x) / 2; | |
d3_geo_centroidY1 += z * (y0 + y) / 2; | |
d3_geo_centroidZ1 += z; | |
z = y0 * x - x0 * y; | |
d3_geo_centroidX2 += z * (x0 + x); | |
d3_geo_centroidY2 += z * (y0 + y); | |
d3_geo_centroidZ2 += z * 3; | |
d3_geo_pathCentroidPoint(x0 = x, y0 = y); | |
} | |
d3_geo_pathCentroid.lineEnd = function() { | |
nextPoint(x00, y00); | |
}; | |
} | |
function d3_geo_pathContext(context) { | |
var pointRadius = 4.5; | |
var stream = { | |
point: point, | |
lineStart: function() { | |
stream.point = pointLineStart; | |
}, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
stream.lineEnd = lineEndPolygon; | |
}, | |
polygonEnd: function() { | |
stream.lineEnd = lineEnd; | |
stream.point = point; | |
}, | |
pointRadius: function(_) { | |
pointRadius = _; | |
return stream; | |
}, | |
result: d3_noop | |
}; | |
function point(x, y) { | |
context.moveTo(x + pointRadius, y); | |
context.arc(x, y, pointRadius, 0, τ); | |
} | |
function pointLineStart(x, y) { | |
context.moveTo(x, y); | |
stream.point = pointLine; | |
} | |
function pointLine(x, y) { | |
context.lineTo(x, y); | |
} | |
function lineEnd() { | |
stream.point = point; | |
} | |
function lineEndPolygon() { | |
context.closePath(); | |
} | |
return stream; | |
} | |
function d3_geo_resample(project) { | |
var δ2 = .5, cosMinDistance = Math.cos(30 * d3_radians), maxDepth = 16; | |
function resample(stream) { | |
return (maxDepth ? resampleRecursive : resampleNone)(stream); | |
} | |
function resampleNone(stream) { | |
return d3_geo_transformPoint(stream, function(x, y) { | |
x = project(x, y); | |
stream.point(x[0], x[1]); | |
}); | |
} | |
function resampleRecursive(stream) { | |
var λ00, φ00, x00, y00, a00, b00, c00, λ0, x0, y0, a0, b0, c0; | |
var resample = { | |
point: point, | |
lineStart: lineStart, | |
lineEnd: lineEnd, | |
polygonStart: function() { | |
stream.polygonStart(); | |
resample.lineStart = ringStart; | |
}, | |
polygonEnd: function() { | |
stream.polygonEnd(); | |
resample.lineStart = lineStart; | |
} | |
}; | |
function point(x, y) { | |
x = project(x, y); | |
stream.point(x[0], x[1]); | |
} | |
function lineStart() { | |
x0 = NaN; | |
resample.point = linePoint; | |
stream.lineStart(); | |
} | |
function linePoint(λ, φ) { | |
var c = d3_geo_cartesian([ λ, φ ]), p = project(λ, φ); | |
resampleLineTo(x0, y0, λ0, a0, b0, c0, x0 = p[0], y0 = p[1], λ0 = λ, a0 = c[0], b0 = c[1], c0 = c[2], maxDepth, stream); | |
stream.point(x0, y0); | |
} | |
function lineEnd() { | |
resample.point = point; | |
stream.lineEnd(); | |
} | |
function ringStart() { | |
lineStart(); | |
resample.point = ringPoint; | |
resample.lineEnd = ringEnd; | |
} | |
function ringPoint(λ, φ) { | |
linePoint(λ00 = λ, φ00 = φ), x00 = x0, y00 = y0, a00 = a0, b00 = b0, c00 = c0; | |
resample.point = linePoint; | |
} | |
function ringEnd() { | |
resampleLineTo(x0, y0, λ0, a0, b0, c0, x00, y00, λ00, a00, b00, c00, maxDepth, stream); | |
resample.lineEnd = lineEnd; | |
lineEnd(); | |
} | |
return resample; | |
} | |
function resampleLineTo(x0, y0, λ0, a0, b0, c0, x1, y1, λ1, a1, b1, c1, depth, stream) { | |
var dx = x1 - x0, dy = y1 - y0, d2 = dx * dx + dy * dy; | |
if (d2 > 4 * δ2 && depth--) { | |
var a = a0 + a1, b = b0 + b1, c = c0 + c1, m = Math.sqrt(a * a + b * b + c * c), φ2 = Math.asin(c /= m), λ2 = abs(abs(c) - 1) < ε || abs(λ0 - λ1) < ε ? (λ0 + λ1) / 2 : Math.atan2(b, a), p = project(λ2, φ2), x2 = p[0], y2 = p[1], dx2 = x2 - x0, dy2 = y2 - y0, dz = dy * dx2 - dx * dy2; | |
if (dz * dz / d2 > δ2 || abs((dx * dx2 + dy * dy2) / d2 - .5) > .3 || a0 * a1 + b0 * b1 + c0 * c1 < cosMinDistance) { | |
resampleLineTo(x0, y0, λ0, a0, b0, c0, x2, y2, λ2, a /= m, b /= m, c, depth, stream); | |
stream.point(x2, y2); | |
resampleLineTo(x2, y2, λ2, a, b, c, x1, y1, λ1, a1, b1, c1, depth, stream); | |
} | |
} | |
} | |
resample.precision = function(_) { | |
if (!arguments.length) return Math.sqrt(δ2); | |
maxDepth = (δ2 = _ * _) > 0 && 16; | |
return resample; | |
}; | |
return resample; | |
} | |
d3.geo.path = function() { | |
var pointRadius = 4.5, projection, context, projectStream, contextStream, cacheStream; | |
function path(object) { | |
if (object) { | |
if (typeof pointRadius === "function") contextStream.pointRadius(+pointRadius.apply(this, arguments)); | |
if (!cacheStream || !cacheStream.valid) cacheStream = projectStream(contextStream); | |
d3.geo.stream(object, cacheStream); | |
} | |
return contextStream.result(); | |
} | |
path.area = function(object) { | |
d3_geo_pathAreaSum = 0; | |
d3.geo.stream(object, projectStream(d3_geo_pathArea)); | |
return d3_geo_pathAreaSum; | |
}; | |
path.centroid = function(object) { | |
d3_geo_centroidX0 = d3_geo_centroidY0 = d3_geo_centroidZ0 = d3_geo_centroidX1 = d3_geo_centroidY1 = d3_geo_centroidZ1 = d3_geo_centroidX2 = d3_geo_centroidY2 = d3_geo_centroidZ2 = 0; | |
d3.geo.stream(object, projectStream(d3_geo_pathCentroid)); | |
return d3_geo_centroidZ2 ? [ d3_geo_centroidX2 / d3_geo_centroidZ2, d3_geo_centroidY2 / d3_geo_centroidZ2 ] : d3_geo_centroidZ1 ? [ d3_geo_centroidX1 / d3_geo_centroidZ1, d3_geo_centroidY1 / d3_geo_centroidZ1 ] : d3_geo_centroidZ0 ? [ d3_geo_centroidX0 / d3_geo_centroidZ0, d3_geo_centroidY0 / d3_geo_centroidZ0 ] : [ NaN, NaN ]; | |
}; | |
path.bounds = function(object) { | |
d3_geo_pathBoundsX1 = d3_geo_pathBoundsY1 = -(d3_geo_pathBoundsX0 = d3_geo_pathBoundsY0 = Infinity); | |
d3.geo.stream(object, projectStream(d3_geo_pathBounds)); | |
return [ [ d3_geo_pathBoundsX0, d3_geo_pathBoundsY0 ], [ d3_geo_pathBoundsX1, d3_geo_pathBoundsY1 ] ]; | |
}; | |
path.projection = function(_) { | |
if (!arguments.length) return projection; | |
projectStream = (projection = _) ? _.stream || d3_geo_pathProjectStream(_) : d3_identity; | |
return reset(); | |
}; | |
path.context = function(_) { | |
if (!arguments.length) return context; | |
contextStream = (context = _) == null ? new d3_geo_pathBuffer() : new d3_geo_pathContext(_); | |
if (typeof pointRadius !== "function") contextStream.pointRadius(pointRadius); | |
return reset(); | |
}; | |
path.pointRadius = function(_) { | |
if (!arguments.length) return pointRadius; | |
pointRadius = typeof _ === "function" ? _ : (contextStream.pointRadius(+_), +_); | |
return path; | |
}; | |
function reset() { | |
cacheStream = null; | |
return path; | |
} | |
return path.projection(d3.geo.albersUsa()).context(null); | |
}; | |
function d3_geo_pathProjectStream(project) { | |
var resample = d3_geo_resample(function(x, y) { | |
return project([ x * d3_degrees, y * d3_degrees ]); | |
}); | |
return function(stream) { | |
return d3_geo_projectionRadians(resample(stream)); | |
}; | |
} | |
d3.geo.transform = function(methods) { | |
return { | |
stream: function(stream) { | |
var transform = new d3_geo_transform(stream); | |
for (var k in methods) transform[k] = methods[k]; | |
return transform; | |
} | |
}; | |
}; | |
function d3_geo_transform(stream) { | |
this.stream = stream; | |
} | |
d3_geo_transform.prototype = { | |
point: function(x, y) { | |
this.stream.point(x, y); | |
}, | |
sphere: function() { | |
this.stream.sphere(); | |
}, | |
lineStart: function() { | |
this.stream.lineStart(); | |
}, | |
lineEnd: function() { | |
this.stream.lineEnd(); | |
}, | |
polygonStart: function() { | |
this.stream.polygonStart(); | |
}, | |
polygonEnd: function() { | |
this.stream.polygonEnd(); | |
} | |
}; | |
function d3_geo_transformPoint(stream, point) { | |
return { | |
point: point, | |
sphere: function() { | |
stream.sphere(); | |
}, | |
lineStart: function() { | |
stream.lineStart(); | |
}, | |
lineEnd: function() { | |
stream.lineEnd(); | |
}, | |
polygonStart: function() { | |
stream.polygonStart(); | |
}, | |
polygonEnd: function() { | |
stream.polygonEnd(); | |
} | |
}; | |
} | |
d3.geo.projection = d3_geo_projection; | |
d3.geo.projectionMutator = d3_geo_projectionMutator; | |
function d3_geo_projection(project) { | |
return d3_geo_projectionMutator(function() { | |
return project; | |
})(); | |
} | |
function d3_geo_projectionMutator(projectAt) { | |
var project, rotate, projectRotate, projectResample = d3_geo_resample(function(x, y) { | |
x = project(x, y); | |
return [ x[0] * k + δx, δy - x[1] * k ]; | |
}), k = 150, x = 480, y = 250, λ = 0, φ = 0, δλ = 0, δφ = 0, δγ = 0, δx, δy, preclip = d3_geo_clipAntimeridian, postclip = d3_identity, clipAngle = null, clipExtent = null, stream; | |
function projection(point) { | |
point = projectRotate(point[0] * d3_radians, point[1] * d3_radians); | |
return [ point[0] * k + δx, δy - point[1] * k ]; | |
} | |
function invert(point) { | |
point = projectRotate.invert((point[0] - δx) / k, (δy - point[1]) / k); | |
return point && [ point[0] * d3_degrees, point[1] * d3_degrees ]; | |
} | |
projection.stream = function(output) { | |
if (stream) stream.valid = false; | |
stream = d3_geo_projectionRadians(preclip(rotate, projectResample(postclip(output)))); | |
stream.valid = true; | |
return stream; | |
}; | |
projection.clipAngle = function(_) { | |
if (!arguments.length) return clipAngle; | |
preclip = _ == null ? (clipAngle = _, d3_geo_clipAntimeridian) : d3_geo_clipCircle((clipAngle = +_) * d3_radians); | |
return invalidate(); | |
}; | |
projection.clipExtent = function(_) { | |
if (!arguments.length) return clipExtent; | |
clipExtent = _; | |
postclip = _ ? d3_geo_clipExtent(_[0][0], _[0][1], _[1][0], _[1][1]) : d3_identity; | |
return invalidate(); | |
}; | |
projection.scale = function(_) { | |
if (!arguments.length) return k; | |
k = +_; | |
return reset(); | |
}; | |
projection.translate = function(_) { | |
if (!arguments.length) return [ x, y ]; | |
x = +_[0]; | |
y = +_[1]; | |
return reset(); | |
}; | |
projection.center = function(_) { | |
if (!arguments.length) return [ λ * d3_degrees, φ * d3_degrees ]; | |
λ = _[0] % 360 * d3_radians; | |
φ = _[1] % 360 * d3_radians; | |
return reset(); | |
}; | |
projection.rotate = function(_) { | |
if (!arguments.length) return [ δλ * d3_degrees, δφ * d3_degrees, δγ * d3_degrees ]; | |
δλ = _[0] % 360 * d3_radians; | |
δφ = _[1] % 360 * d3_radians; | |
δγ = _.length > 2 ? _[2] % 360 * d3_radians : 0; | |
return reset(); | |
}; | |
d3.rebind(projection, projectResample, "precision"); | |
function reset() { | |
projectRotate = d3_geo_compose(rotate = d3_geo_rotation(δλ, δφ, δγ), project); | |
var center = project(λ, φ); | |
δx = x - center[0] * k; | |
δy = y + center[1] * k; | |
return invalidate(); | |
} | |
function invalidate() { | |
if (stream) stream.valid = false, stream = null; | |
return projection; | |
} | |
return function() { | |
project = projectAt.apply(this, arguments); | |
projection.invert = project.invert && invert; | |
return reset(); | |
}; | |
} | |
function d3_geo_projectionRadians(stream) { | |
return d3_geo_transformPoint(stream, function(x, y) { | |
stream.point(x * d3_radians, y * d3_radians); | |
}); | |
} | |
function d3_geo_equirectangular(λ, φ) { | |
return [ λ, φ ]; | |
} | |
(d3.geo.equirectangular = function() { | |
return d3_geo_projection(d3_geo_equirectangular); | |
}).raw = d3_geo_equirectangular.invert = d3_geo_equirectangular; | |
d3.geo.rotation = function(rotate) { | |
rotate = d3_geo_rotation(rotate[0] % 360 * d3_radians, rotate[1] * d3_radians, rotate.length > 2 ? rotate[2] * d3_radians : 0); | |
function forward(coordinates) { | |
coordinates = rotate(coordinates[0] * d3_radians, coordinates[1] * d3_radians); | |
return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; | |
} | |
forward.invert = function(coordinates) { | |
coordinates = rotate.invert(coordinates[0] * d3_radians, coordinates[1] * d3_radians); | |
return coordinates[0] *= d3_degrees, coordinates[1] *= d3_degrees, coordinates; | |
}; | |
return forward; | |
}; | |
function d3_geo_identityRotation(λ, φ) { | |
return [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; | |
} | |
d3_geo_identityRotation.invert = d3_geo_equirectangular; | |
function d3_geo_rotation(δλ, δφ, δγ) { | |
return δλ ? δφ || δγ ? d3_geo_compose(d3_geo_rotationλ(δλ), d3_geo_rotationφγ(δφ, δγ)) : d3_geo_rotationλ(δλ) : δφ || δγ ? d3_geo_rotationφγ(δφ, δγ) : d3_geo_identityRotation; | |
} | |
function d3_geo_forwardRotationλ(δλ) { | |
return function(λ, φ) { | |
return λ += δλ, [ λ > π ? λ - τ : λ < -π ? λ + τ : λ, φ ]; | |
}; | |
} | |
function d3_geo_rotationλ(δλ) { | |
var rotation = d3_geo_forwardRotationλ(δλ); | |
rotation.invert = d3_geo_forwardRotationλ(-δλ); | |
return rotation; | |
} | |
function d3_geo_rotationφγ(δφ, δγ) { | |
var cosδφ = Math.cos(δφ), sinδφ = Math.sin(δφ), cosδγ = Math.cos(δγ), sinδγ = Math.sin(δγ); | |
function rotation(λ, φ) { | |
var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδφ + x * sinδφ; | |
return [ Math.atan2(y * cosδγ - k * sinδγ, x * cosδφ - z * sinδφ), d3_asin(k * cosδγ + y * sinδγ) ]; | |
} | |
rotation.invert = function(λ, φ) { | |
var cosφ = Math.cos(φ), x = Math.cos(λ) * cosφ, y = Math.sin(λ) * cosφ, z = Math.sin(φ), k = z * cosδγ - y * sinδγ; | |
return [ Math.atan2(y * cosδγ + z * sinδγ, x * cosδφ + k * sinδφ), d3_asin(k * cosδφ - x * sinδφ) ]; | |
}; | |
return rotation; | |
} | |
d3.geo.circle = function() { | |
var origin = [ 0, 0 ], angle, precision = 6, interpolate; | |
function circle() { | |
var center = typeof origin === "function" ? origin.apply(this, arguments) : origin, rotate = d3_geo_rotation(-center[0] * d3_radians, -center[1] * d3_radians, 0).invert, ring = []; | |
interpolate(null, null, 1, { | |
point: function(x, y) { | |
ring.push(x = rotate(x, y)); | |
x[0] *= d3_degrees, x[1] *= d3_degrees; | |
} | |
}); | |
return { | |
type: "Polygon", | |
coordinates: [ ring ] | |
}; | |
} | |
circle.origin = function(x) { | |
if (!arguments.length) return origin; | |
origin = x; | |
return circle; | |
}; | |
circle.angle = function(x) { | |
if (!arguments.length) return angle; | |
interpolate = d3_geo_circleInterpolate((angle = +x) * d3_radians, precision * d3_radians); | |
return circle; | |
}; | |
circle.precision = function(_) { | |
if (!arguments.length) return precision; | |
interpolate = d3_geo_circleInterpolate(angle * d3_radians, (precision = +_) * d3_radians); | |
return circle; | |
}; | |
return circle.angle(90); | |
}; | |
function d3_geo_circleInterpolate(radius, precision) { | |
var cr = Math.cos(radius), sr = Math.sin(radius); | |
return function(from, to, direction, listener) { | |
var step = direction * precision; | |
if (from != null) { | |
from = d3_geo_circleAngle(cr, from); | |
to = d3_geo_circleAngle(cr, to); | |
if (direction > 0 ? from < to : from > to) from += direction * τ; | |
} else { | |
from = radius + direction * τ; | |
to = radius - .5 * step; | |
} | |
for (var point, t = from; direction > 0 ? t > to : t < to; t -= step) { | |
listener.point((point = d3_geo_spherical([ cr, -sr * Math.cos(t), -sr * Math.sin(t) ]))[0], point[1]); | |
} | |
}; | |
} | |
function d3_geo_circleAngle(cr, point) { | |
var a = d3_geo_cartesian(point); | |
a[0] -= cr; | |
d3_geo_cartesianNormalize(a); | |
var angle = d3_acos(-a[1]); | |
return ((-a[2] < 0 ? -angle : angle) + 2 * Math.PI - ε) % (2 * Math.PI); | |
} | |
d3.geo.distance = function(a, b) { | |
var Δλ = (b[0] - a[0]) * d3_radians, φ0 = a[1] * d3_radians, φ1 = b[1] * d3_radians, sinΔλ = Math.sin(Δλ), cosΔλ = Math.cos(Δλ), sinφ0 = Math.sin(φ0), cosφ0 = Math.cos(φ0), sinφ1 = Math.sin(φ1), cosφ1 = Math.cos(φ1), t; | |
return Math.atan2(Math.sqrt((t = cosφ1 * sinΔλ) * t + (t = cosφ0 * sinφ1 - sinφ0 * cosφ1 * cosΔλ) * t), sinφ0 * sinφ1 + cosφ0 * cosφ1 * cosΔλ); | |
}; | |
d3.geo.graticule = function() { | |
var x1, x0, X1, X0, y1, y0, Y1, Y0, dx = 10, dy = dx, DX = 90, DY = 360, x, y, X, Y, precision = 2.5; | |
function graticule() { | |
return { | |
type: "MultiLineString", | |
coordinates: lines() | |
}; | |
} | |
function lines() { | |
return d3.range(Math.ceil(X0 / DX) * DX, X1, DX).map(X).concat(d3.range(Math.ceil(Y0 / DY) * DY, Y1, DY).map(Y)).concat(d3.range(Math.ceil(x0 / dx) * dx, x1, dx).filter(function(x) { | |
return abs(x % DX) > ε; | |
}).map(x)).concat(d3.range(Math.ceil(y0 / dy) * dy, y1, dy).filter(function(y) { | |
return abs(y % DY) > ε; | |
}).map(y)); | |
} | |
graticule.lines = function() { | |
return lines().map(function(coordinates) { | |
return { | |
type: "LineString", | |
coordinates: coordinates | |
}; | |
}); | |
}; | |
graticule.outline = function() { | |
return { | |
type: "Polygon", | |
coordinates: [ X(X0).concat(Y(Y1).slice(1), X(X1).reverse().slice(1), Y(Y0).reverse().slice(1)) ] | |
}; | |
}; | |
graticule.extent = function(_) { | |
if (!arguments.length) return graticule.minorExtent(); | |
return graticule.majorExtent(_).minorExtent(_); | |
}; | |
graticule.majorExtent = function(_) { | |
if (!arguments.length) return [ [ X0, Y0 ], [ X1, Y1 ] ]; | |
X0 = +_[0][0], X1 = +_[1][0]; | |
Y0 = +_[0][1], Y1 = +_[1][1]; | |
if (X0 > X1) _ = X0, X0 = X1, X1 = _; | |
if (Y0 > Y1) _ = Y0, Y0 = Y1, Y1 = _; | |
return graticule.precision(precision); | |
}; | |
graticule.minorExtent = function(_) { | |
if (!arguments.length) return [ [ x0, y0 ], [ x1, y1 ] ]; | |
x0 = +_[0][0], x1 = +_[1][0]; | |
y0 = +_[0][1], y1 = +_[1][1]; | |
if (x0 > x1) _ = x0, x0 = x1, x1 = _; | |
if (y0 > y1) _ = y0, y0 = y1, y1 = _; | |
return graticule.precision(precision); | |
}; | |
graticule.step = function(_) { | |
if (!arguments.length) return graticule.minorStep(); | |
return graticule.majorStep(_).minorStep(_); | |
}; | |
graticule.majorStep = function(_) { | |
if (!arguments.length) return [ DX, DY ]; | |
DX = +_[0], DY = +_[1]; | |
return graticule; | |
}; | |
graticule.minorStep = function(_) { | |
if (!arguments.length) return [ dx, dy ]; | |
dx = +_[0], dy = +_[1]; | |
return graticule; | |
}; | |
graticule.precision = function(_) { | |
if (!arguments.length) return precision; | |
precision = +_; | |
x = d3_geo_graticuleX(y0, y1, 90); | |
y = d3_geo_graticuleY(x0, x1, precision); | |
X = d3_geo_graticuleX(Y0, Y1, 90); | |
Y = d3_geo_graticuleY(X0, X1, precision); | |
return graticule; | |
}; | |
return graticule.majorExtent([ [ -180, -90 + ε ], [ 180, 90 - ε ] ]).minorExtent([ [ -180, -80 - ε ], [ 180, 80 + ε ] ]); | |
}; | |
function d3_geo_graticuleX(y0, y1, dy) { | |
var y = d3.range(y0, y1 - ε, dy).concat(y1); | |
return function(x) { | |
return y.map(function(y) { | |
return [ x, y ]; | |
}); | |
}; | |
} | |
function d3_geo_graticuleY(x0, x1, dx) { | |
var x = d3.range(x0, x1 - ε, dx).concat(x1); | |
return function(y) { | |
return x.map(function(x) { | |
return [ x, y ]; | |
}); | |
}; | |
} | |
function d3_source(d) { | |
return d.source; | |
} | |
function d3_target(d) { | |
return d.target; | |
} | |
d3.geo.greatArc = function() { | |
var source = d3_source, source_, target = d3_target, target_; | |
function greatArc() { | |
return { | |
type: "LineString", | |
coordinates: [ source_ || source.apply(this, arguments), target_ || target.apply(this, arguments) ] | |
}; | |
} | |
greatArc.distance = function() { | |
return d3.geo.distance(source_ || source.apply(this, arguments), target_ || target.apply(this, arguments)); | |
}; | |
greatArc.source = function(_) { | |
if (!arguments.length) return source; | |
source = _, source_ = typeof _ === "function" ? null : _; | |
return greatArc; | |
}; | |
greatArc.target = function(_) { | |
if (!arguments.length) return target; | |
target = _, target_ = typeof _ === "function" ? null : _; | |
return greatArc; | |
}; | |
greatArc.precision = function() { | |
return arguments.length ? greatArc : 0; | |
}; | |
return greatArc; | |
}; | |
d3.geo.interpolate = function(source, target) { | |
return d3_geo_interpolate(source[0] * d3_radians, source[1] * d3_radians, target[0] * d3_radians, target[1] * d3_radians); | |
}; | |
function d3_geo_interpolate(x0, y0, x1, y1) { | |
var cy0 = Math.cos(y0), sy0 = Math.sin(y0), cy1 = Math.cos(y1), sy1 = Math.sin(y1), kx0 = cy0 * Math.cos(x0), ky0 = cy0 * Math.sin(x0), kx1 = cy1 * Math.cos(x1), ky1 = cy1 * Math.sin(x1), d = 2 * Math.asin(Math.sqrt(d3_haversin(y1 - y0) + cy0 * cy1 * d3_haversin(x1 - x0))), k = 1 / Math.sin(d); | |
var interpolate = d ? function(t) { | |
var B = Math.sin(t *= d) * k, A = Math.sin(d - t) * k, x = A * kx0 + B * kx1, y = A * ky0 + B * ky1, z = A * sy0 + B * sy1; | |
return [ Math.atan2(y, x) * d3_degrees, Math.atan2(z, Math.sqrt(x * x + y * y)) * d3_degrees ]; | |
} : function() { | |
return [ x0 * d3_degrees, y0 * d3_degrees ]; | |
}; | |
interpolate.distance = d; | |
return interpolate; | |
} | |
d3.geo.length = function(object) { | |
d3_geo_lengthSum = 0; | |
d3.geo.stream(object, d3_geo_length); | |
return d3_geo_lengthSum; | |
}; | |
var d3_geo_lengthSum; | |
var d3_geo_length = { | |
sphere: d3_noop, | |
point: d3_noop, | |
lineStart: d3_geo_lengthLineStart, | |
lineEnd: d3_noop, | |
polygonStart: d3_noop, | |
polygonEnd: d3_noop | |
}; | |
function d3_geo_lengthLineStart() { | |
var λ0, sinφ0, cosφ0; | |
d3_geo_length.point = function(λ, φ) { | |
λ0 = λ * d3_radians, sinφ0 = Math.sin(φ *= d3_radians), cosφ0 = Math.cos(φ); | |
d3_geo_length.point = nextPoint; | |
}; | |
d3_geo_length.lineEnd = function() { | |
d3_geo_length.point = d3_geo_length.lineEnd = d3_noop; | |
}; | |
function nextPoint(λ, φ) { | |
var sinφ = Math.sin(φ *= d3_radians), cosφ = Math.cos(φ), t = abs((λ *= d3_radians) - λ0), cosΔλ = Math.cos(t); | |
d3_geo_lengthSum += Math.atan2(Math.sqrt((t = cosφ * Math.sin(t)) * t + (t = cosφ0 * sinφ - sinφ0 * cosφ * cosΔλ) * t), sinφ0 * sinφ + cosφ0 * cosφ * cosΔλ); | |
λ0 = λ, sinφ0 = sinφ, cosφ0 = cosφ; | |
} | |
} | |
function d3_geo_azimuthal(scale, angle) { | |
function azimuthal(λ, φ) { | |
var cosλ = Math.cos(λ), cosφ = Math.cos(φ), k = scale(cosλ * cosφ); | |
return [ k * cosφ * Math.sin(λ), k * Math.sin(φ) ]; | |
} | |
azimuthal.invert = function(x, y) { | |
var ρ = Math.sqrt(x * x + y * y), c = angle(ρ), sinc = Math.sin(c), cosc = Math.cos(c); | |
return [ Math.atan2(x * sinc, ρ * cosc), Math.asin(ρ && y * sinc / ρ) ]; | |
}; | |
return azimuthal; | |
} | |
var d3_geo_azimuthalEqualArea = d3_geo_azimuthal(function(cosλcosφ) { | |
return Math.sqrt(2 / (1 + cosλcosφ)); | |
}, function(ρ) { | |
return 2 * Math.asin(ρ / 2); | |
}); | |
(d3.geo.azimuthalEqualArea = function() { | |
return d3_geo_projection(d3_geo_azimuthalEqualArea); | |
}).raw = d3_geo_azimuthalEqualArea; | |
var d3_geo_azimuthalEquidistant = d3_geo_azimuthal(function(cosλcosφ) { | |
var c = Math.acos(cosλcosφ); | |
return c && c / Math.sin(c); | |
}, d3_identity); | |
(d3.geo.azimuthalEquidistant = function() { | |
return d3_geo_projection(d3_geo_azimuthalEquidistant); | |
}).raw = d3_geo_azimuthalEquidistant; | |
function d3_geo_conicConformal(φ0, φ1) { | |
var cosφ0 = Math.cos(φ0), t = function(φ) { | |
return Math.tan(π / 4 + φ / 2); | |
}, n = φ0 === φ1 ? Math.sin(φ0) : Math.log(cosφ0 / Math.cos(φ1)) / Math.log(t(φ1) / t(φ0)), F = cosφ0 * Math.pow(t(φ0), n) / n; | |
if (!n) return d3_geo_mercator; | |
function forward(λ, φ) { | |
if (F > 0) { | |
if (φ < -halfπ + ε) φ = -halfπ + ε; | |
} else { | |
if (φ > halfπ - ε) φ = halfπ - ε; | |
} | |
var ρ = F / Math.pow(t(φ), n); | |
return [ ρ * Math.sin(n * λ), F - ρ * Math.cos(n * λ) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ0_y = F - y, ρ = d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y); | |
return [ Math.atan2(x, ρ0_y) / n, 2 * Math.atan(Math.pow(F / ρ, 1 / n)) - halfπ ]; | |
}; | |
return forward; | |
} | |
(d3.geo.conicConformal = function() { | |
return d3_geo_conic(d3_geo_conicConformal); | |
}).raw = d3_geo_conicConformal; | |
function d3_geo_conicEquidistant(φ0, φ1) { | |
var cosφ0 = Math.cos(φ0), n = φ0 === φ1 ? Math.sin(φ0) : (cosφ0 - Math.cos(φ1)) / (φ1 - φ0), G = cosφ0 / n + φ0; | |
if (abs(n) < ε) return d3_geo_equirectangular; | |
function forward(λ, φ) { | |
var ρ = G - φ; | |
return [ ρ * Math.sin(n * λ), G - ρ * Math.cos(n * λ) ]; | |
} | |
forward.invert = function(x, y) { | |
var ρ0_y = G - y; | |
return [ Math.atan2(x, ρ0_y) / n, G - d3_sgn(n) * Math.sqrt(x * x + ρ0_y * ρ0_y) ]; | |
}; | |
return forward; | |
} | |
(d3.geo.conicEquidistant = function() { | |
return d3_geo_conic(d3_geo_conicEquidistant); | |
}).raw = d3_geo_conicEquidistant; | |
var d3_geo_gnomonic = d3_geo_azimuthal(function(cosλcosφ) { | |
return 1 / cosλcosφ; | |
}, Math.atan); | |
(d3.geo.gnomonic = function() { | |
return d3_geo_projection(d3_geo_gnomonic); | |
}).raw = d3_geo_gnomonic; | |
function d3_geo_mercator(λ, φ) { | |
return [ λ, Math.log(Math.tan(π / 4 + φ / 2)) ]; | |
} | |
d3_geo_mercator.invert = function(x, y) { | |
return [ x, 2 * Math.atan(Math.exp(y)) - halfπ ]; | |
}; | |
function d3_geo_mercatorProjection(project) { | |
var m = d3_geo_projection(project), scale = m.scale, translate = m.translate, clipExtent = m.clipExtent, clipAuto; | |
m.scale = function() { | |
var v = scale.apply(m, arguments); | |
return v === m ? clipAuto ? m.clipExtent(null) : m : v; | |
}; | |
m.translate = function() { | |
var v = translate.apply(m, arguments); | |
return v === m ? clipAuto ? m.clipExtent(null) : m : v; | |
}; | |
m.clipExtent = function(_) { | |
var v = clipExtent.apply(m, arguments); | |
if (v === m) { | |
if (clipAuto = _ == null) { | |
var k = π * scale(), t = translate(); | |
clipExtent([ [ t[0] - k, t[1] - k ], [ t[0] + k, t[1] + k ] ]); | |
} | |
} else if (clipAuto) { | |
v = null; | |
} | |
return v; | |
}; | |
return m.clipExtent(null); | |
} | |
(d3.geo.mercator = function() { | |
return d3_geo_mercatorProjection(d3_geo_mercator); | |
}).raw = d3_geo_mercator; | |
var d3_geo_orthographic = d3_geo_azimuthal(function() { | |
return 1; | |
}, Math.asin); | |
(d3.geo.orthographic = function() { | |
return d3_geo_projection(d3_geo_orthographic); | |
}).raw = d3_geo_orthographic; | |
var d3_geo_stereographic = d3_geo_azimuthal(function(cosλcosφ) { | |
return 1 / (1 + cosλcosφ); | |
}, function(ρ) { | |
return 2 * Math.atan(ρ); | |
}); | |
(d3.geo.stereographic = function() { | |
return d3_geo_projection(d3_geo_stereographic); | |
}).raw = d3_geo_stereographic; | |
function d3_geo_transverseMercator(λ, φ) { | |
return [ Math.log(Math.tan(π / 4 + φ / 2)), -λ ]; | |
} | |
d3_geo_transverseMercator.invert = function(x, y) { | |
return [ -y, 2 * Math.atan(Math.exp(x)) - halfπ ]; | |
}; | |
(d3.geo.transverseMercator = function() { | |
var projection = d3_geo_mercatorProjection(d3_geo_transverseMercator), center = projection.center, rotate = projection.rotate; | |
projection.center = function(_) { | |
return _ ? center([ -_[1], _[0] ]) : (_ = center(), [ _[1], -_[0] ]); | |
}; | |
projection.rotate = function(_) { | |
return _ ? rotate([ _[0], _[1], _.length > 2 ? _[2] + 90 : 90 ]) : (_ = rotate(), | |
[ _[0], _[1], _[2] - 90 ]); | |
}; | |
return rotate([ 0, 0, 90 ]); | |
}).raw = d3_geo_transverseMercator; | |
d3.geom = {}; | |
function d3_geom_pointX(d) { | |
return d[0]; | |
} | |
function d3_geom_pointY(d) { | |
return d[1]; | |
} | |
d3.geom.hull = function(vertices) { | |
var x = d3_geom_pointX, y = d3_geom_pointY; | |
if (arguments.length) return hull(vertices); | |
function hull(data) { | |
if (data.length < 3) return []; | |
var fx = d3_functor(x), fy = d3_functor(y), i, n = data.length, points = [], flippedPoints = []; | |
for (i = 0; i < n; i++) { | |
points.push([ +fx.call(this, data[i], i), +fy.call(this, data[i], i), i ]); | |
} | |
points.sort(d3_geom_hullOrder); | |
for (i = 0; i < n; i++) flippedPoints.push([ points[i][0], -points[i][1] ]); | |
var upper = d3_geom_hullUpper(points), lower = d3_geom_hullUpper(flippedPoints); | |
var skipLeft = lower[0] === upper[0], skipRight = lower[lower.length - 1] === upper[upper.length - 1], polygon = []; | |
for (i = upper.length - 1; i >= 0; --i) polygon.push(data[points[upper[i]][2]]); | |
for (i = +skipLeft; i < lower.length - skipRight; ++i) polygon.push(data[points[lower[i]][2]]); | |
return polygon; | |
} | |
hull.x = function(_) { | |
return arguments.length ? (x = _, hull) : x; | |
}; | |
hull.y = function(_) { | |
return arguments.length ? (y = _, hull) : y; | |
}; | |
return hull; | |
}; | |
function d3_geom_hullUpper(points) { | |
var n = points.length, hull = [ 0, 1 ], hs = 2; | |
for (var i = 2; i < n; i++) { | |
while (hs > 1 && d3_cross2d(points[hull[hs - 2]], points[hull[hs - 1]], points[i]) <= 0) --hs; | |
hull[hs++] = i; | |
} | |
return hull.slice(0, hs); | |
} | |
function d3_geom_hullOrder(a, b) { | |
return a[0] - b[0] || a[1] - b[1]; | |
} | |
d3.geom.polygon = function(coordinates) { | |
d3_subclass(coordinates, d3_geom_polygonPrototype); | |
return coordinates; | |
}; | |
var d3_geom_polygonPrototype = d3.geom.polygon.prototype = []; | |
d3_geom_polygonPrototype.area = function() { | |
var i = -1, n = this.length, a, b = this[n - 1], area = 0; | |
while (++i < n) { | |
a = b; | |
b = this[i]; | |
area += a[1] * b[0] - a[0] * b[1]; | |
} | |
return area * .5; | |
}; | |
d3_geom_polygonPrototype.centroid = function(k) { | |
var i = -1, n = this.length, x = 0, y = 0, a, b = this[n - 1], c; | |
if (!arguments.length) k = -1 / (6 * this.area()); | |
while (++i < n) { | |
a = b; | |
b = this[i]; | |
c = a[0] * b[1] - b[0] * a[1]; | |
x += (a[0] + b[0]) * c; | |
y += (a[1] + b[1]) * c; | |
} | |
return [ x * k, y * k ]; | |
}; | |
d3_geom_polygonPrototype.clip = function(subject) { | |
var input, closed = d3_geom_polygonClosed(subject), i = -1, n = this.length - d3_geom_polygonClosed(this), j, m, a = this[n - 1], b, c, d; | |
while (++i < n) { | |
input = subject.slice(); | |
subject.length = 0; | |
b = this[i]; | |
c = input[(m = input.length - closed) - 1]; | |
j = -1; | |
while (++j < m) { | |
d = input[j]; | |
if (d3_geom_polygonInside(d, a, b)) { | |
if (!d3_geom_polygonInside(c, a, b)) { | |
subject.push(d3_geom_polygonIntersect(c, d, a, b)); | |
} | |
subject.push(d); | |
} else if (d3_geom_polygonInside(c, a, b)) { | |
subject.push(d3_geom_polygonIntersect(c, d, a, b)); | |
} | |
c = d; | |
} | |
if (closed) subject.push(subject[0]); | |
a = b; | |
} | |
return subject; | |
}; | |
function d3_geom_polygonInside(p, a, b) { | |
return (b[0] - a[0]) * (p[1] - a[1]) < (b[1] - a[1]) * (p[0] - a[0]); | |
} | |
function d3_geom_polygonIntersect(c, d, a, b) { | |
var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); | |
return [ x1 + ua * x21, y1 + ua * y21 ]; | |
} | |
function d3_geom_polygonClosed(coordinates) { | |
var a = coordinates[0], b = coordinates[coordinates.length - 1]; | |
return !(a[0] - b[0] || a[1] - b[1]); | |
} | |
var d3_geom_voronoiEdges, d3_geom_voronoiCells, d3_geom_voronoiBeaches, d3_geom_voronoiBeachPool = [], d3_geom_voronoiFirstCircle, d3_geom_voronoiCircles, d3_geom_voronoiCirclePool = []; | |
function d3_geom_voronoiBeach() { | |
d3_geom_voronoiRedBlackNode(this); | |
this.edge = this.site = this.circle = null; | |
} | |
function d3_geom_voronoiCreateBeach(site) { | |
var beach = d3_geom_voronoiBeachPool.pop() || new d3_geom_voronoiBeach(); | |
beach.site = site; | |
return beach; | |
} | |
function d3_geom_voronoiDetachBeach(beach) { | |
d3_geom_voronoiDetachCircle(beach); | |
d3_geom_voronoiBeaches.remove(beach); | |
d3_geom_voronoiBeachPool.push(beach); | |
d3_geom_voronoiRedBlackNode(beach); | |
} | |
function d3_geom_voronoiRemoveBeach(beach) { | |
var circle = beach.circle, x = circle.x, y = circle.cy, vertex = { | |
x: x, | |
y: y | |
}, previous = beach.P, next = beach.N, disappearing = [ beach ]; | |
d3_geom_voronoiDetachBeach(beach); | |
var lArc = previous; | |
while (lArc.circle && abs(x - lArc.circle.x) < ε && abs(y - lArc.circle.cy) < ε) { | |
previous = lArc.P; | |
disappearing.unshift(lArc); | |
d3_geom_voronoiDetachBeach(lArc); | |
lArc = previous; | |
} | |
disappearing.unshift(lArc); | |
d3_geom_voronoiDetachCircle(lArc); | |
var rArc = next; | |
while (rArc.circle && abs(x - rArc.circle.x) < ε && abs(y - rArc.circle.cy) < ε) { | |
next = rArc.N; | |
disappearing.push(rArc); | |
d3_geom_voronoiDetachBeach(rArc); | |
rArc = next; | |
} | |
disappearing.push(rArc); | |
d3_geom_voronoiDetachCircle(rArc); | |
var nArcs = disappearing.length, iArc; | |
for (iArc = 1; iArc < nArcs; ++iArc) { | |
rArc = disappearing[iArc]; | |
lArc = disappearing[iArc - 1]; | |
d3_geom_voronoiSetEdgeEnd(rArc.edge, lArc.site, rArc.site, vertex); | |
} | |
lArc = disappearing[0]; | |
rArc = disappearing[nArcs - 1]; | |
rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, rArc.site, null, vertex); | |
d3_geom_voronoiAttachCircle(lArc); | |
d3_geom_voronoiAttachCircle(rArc); | |
} | |
function d3_geom_voronoiAddBeach(site) { | |
var x = site.x, directrix = site.y, lArc, rArc, dxl, dxr, node = d3_geom_voronoiBeaches._; | |
while (node) { | |
dxl = d3_geom_voronoiLeftBreakPoint(node, directrix) - x; | |
if (dxl > ε) node = node.L; else { | |
dxr = x - d3_geom_voronoiRightBreakPoint(node, directrix); | |
if (dxr > ε) { | |
if (!node.R) { | |
lArc = node; | |
break; | |
} | |
node = node.R; | |
} else { | |
if (dxl > -ε) { | |
lArc = node.P; | |
rArc = node; | |
} else if (dxr > -ε) { | |
lArc = node; | |
rArc = node.N; | |
} else { | |
lArc = rArc = node; | |
} | |
break; | |
} | |
} | |
} | |
var newArc = d3_geom_voronoiCreateBeach(site); | |
d3_geom_voronoiBeaches.insert(lArc, newArc); | |
if (!lArc && !rArc) return; | |
if (lArc === rArc) { | |
d3_geom_voronoiDetachCircle(lArc); | |
rArc = d3_geom_voronoiCreateBeach(lArc.site); | |
d3_geom_voronoiBeaches.insert(newArc, rArc); | |
newArc.edge = rArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); | |
d3_geom_voronoiAttachCircle(lArc); | |
d3_geom_voronoiAttachCircle(rArc); | |
return; | |
} | |
if (!rArc) { | |
newArc.edge = d3_geom_voronoiCreateEdge(lArc.site, newArc.site); | |
return; | |
} | |
d3_geom_voronoiDetachCircle(lArc); | |
d3_geom_voronoiDetachCircle(rArc); | |
var lSite = lArc.site, ax = lSite.x, ay = lSite.y, bx = site.x - ax, by = site.y - ay, rSite = rArc.site, cx = rSite.x - ax, cy = rSite.y - ay, d = 2 * (bx * cy - by * cx), hb = bx * bx + by * by, hc = cx * cx + cy * cy, vertex = { | |
x: (cy * hb - by * hc) / d + ax, | |
y: (bx * hc - cx * hb) / d + ay | |
}; | |
d3_geom_voronoiSetEdgeEnd(rArc.edge, lSite, rSite, vertex); | |
newArc.edge = d3_geom_voronoiCreateEdge(lSite, site, null, vertex); | |
rArc.edge = d3_geom_voronoiCreateEdge(site, rSite, null, vertex); | |
d3_geom_voronoiAttachCircle(lArc); | |
d3_geom_voronoiAttachCircle(rArc); | |
} | |
function d3_geom_voronoiLeftBreakPoint(arc, directrix) { | |
var site = arc.site, rfocx = site.x, rfocy = site.y, pby2 = rfocy - directrix; | |
if (!pby2) return rfocx; | |
var lArc = arc.P; | |
if (!lArc) return -Infinity; | |
site = lArc.site; | |
var lfocx = site.x, lfocy = site.y, plby2 = lfocy - directrix; | |
if (!plby2) return lfocx; | |
var hl = lfocx - rfocx, aby2 = 1 / pby2 - 1 / plby2, b = hl / plby2; | |
if (aby2) return (-b + Math.sqrt(b * b - 2 * aby2 * (hl * hl / (-2 * plby2) - lfocy + plby2 / 2 + rfocy - pby2 / 2))) / aby2 + rfocx; | |
return (rfocx + lfocx) / 2; | |
} | |
function d3_geom_voronoiRightBreakPoint(arc, directrix) { | |
var rArc = arc.N; | |
if (rArc) return d3_geom_voronoiLeftBreakPoint(rArc, directrix); | |
var site = arc.site; | |
return site.y === directrix ? site.x : Infinity; | |
} | |
function d3_geom_voronoiCell(site) { | |
this.site = site; | |
this.edges = []; | |
} | |
d3_geom_voronoiCell.prototype.prepare = function() { | |
var halfEdges = this.edges, iHalfEdge = halfEdges.length, edge; | |
while (iHalfEdge--) { | |
edge = halfEdges[iHalfEdge].edge; | |
if (!edge.b || !edge.a) halfEdges.splice(iHalfEdge, 1); | |
} | |
halfEdges.sort(d3_geom_voronoiHalfEdgeOrder); | |
return halfEdges.length; | |
}; | |
function d3_geom_voronoiCloseCells(extent) { | |
var x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], x2, y2, x3, y3, cells = d3_geom_voronoiCells, iCell = cells.length, cell, iHalfEdge, halfEdges, nHalfEdges, start, end; | |
while (iCell--) { | |
cell = cells[iCell]; | |
if (!cell || !cell.prepare()) continue; | |
halfEdges = cell.edges; | |
nHalfEdges = halfEdges.length; | |
iHalfEdge = 0; | |
while (iHalfEdge < nHalfEdges) { | |
end = halfEdges[iHalfEdge].end(), x3 = end.x, y3 = end.y; | |
start = halfEdges[++iHalfEdge % nHalfEdges].start(), x2 = start.x, y2 = start.y; | |
if (abs(x3 - x2) > ε || abs(y3 - y2) > ε) { | |
halfEdges.splice(iHalfEdge, 0, new d3_geom_voronoiHalfEdge(d3_geom_voronoiCreateBorderEdge(cell.site, end, abs(x3 - x0) < ε && y1 - y3 > ε ? { | |
x: x0, | |
y: abs(x2 - x0) < ε ? y2 : y1 | |
} : abs(y3 - y1) < ε && x1 - x3 > ε ? { | |
x: abs(y2 - y1) < ε ? x2 : x1, | |
y: y1 | |
} : abs(x3 - x1) < ε && y3 - y0 > ε ? { | |
x: x1, | |
y: abs(x2 - x1) < ε ? y2 : y0 | |
} : abs(y3 - y0) < ε && x3 - x0 > ε ? { | |
x: abs(y2 - y0) < ε ? x2 : x0, | |
y: y0 | |
} : null), cell.site, null)); | |
++nHalfEdges; | |
} | |
} | |
} | |
} | |
function d3_geom_voronoiHalfEdgeOrder(a, b) { | |
return b.angle - a.angle; | |
} | |
function d3_geom_voronoiCircle() { | |
d3_geom_voronoiRedBlackNode(this); | |
this.x = this.y = this.arc = this.site = this.cy = null; | |
} | |
function d3_geom_voronoiAttachCircle(arc) { | |
var lArc = arc.P, rArc = arc.N; | |
if (!lArc || !rArc) return; | |
var lSite = lArc.site, cSite = arc.site, rSite = rArc.site; | |
if (lSite === rSite) return; | |
var bx = cSite.x, by = cSite.y, ax = lSite.x - bx, ay = lSite.y - by, cx = rSite.x - bx, cy = rSite.y - by; | |
var d = 2 * (ax * cy - ay * cx); | |
if (d >= -ε2) return; | |
var ha = ax * ax + ay * ay, hc = cx * cx + cy * cy, x = (cy * ha - ay * hc) / d, y = (ax * hc - cx * ha) / d, cy = y + by; | |
var circle = d3_geom_voronoiCirclePool.pop() || new d3_geom_voronoiCircle(); | |
circle.arc = arc; | |
circle.site = cSite; | |
circle.x = x + bx; | |
circle.y = cy + Math.sqrt(x * x + y * y); | |
circle.cy = cy; | |
arc.circle = circle; | |
var before = null, node = d3_geom_voronoiCircles._; | |
while (node) { | |
if (circle.y < node.y || circle.y === node.y && circle.x <= node.x) { | |
if (node.L) node = node.L; else { | |
before = node.P; | |
break; | |
} | |
} else { | |
if (node.R) node = node.R; else { | |
before = node; | |
break; | |
} | |
} | |
} | |
d3_geom_voronoiCircles.insert(before, circle); | |
if (!before) d3_geom_voronoiFirstCircle = circle; | |
} | |
function d3_geom_voronoiDetachCircle(arc) { | |
var circle = arc.circle; | |
if (circle) { | |
if (!circle.P) d3_geom_voronoiFirstCircle = circle.N; | |
d3_geom_voronoiCircles.remove(circle); | |
d3_geom_voronoiCirclePool.push(circle); | |
d3_geom_voronoiRedBlackNode(circle); | |
arc.circle = null; | |
} | |
} | |
function d3_geom_voronoiClipEdges(extent) { | |
var edges = d3_geom_voronoiEdges, clip = d3_geom_clipLine(extent[0][0], extent[0][1], extent[1][0], extent[1][1]), i = edges.length, e; | |
while (i--) { | |
e = edges[i]; | |
if (!d3_geom_voronoiConnectEdge(e, extent) || !clip(e) || abs(e.a.x - e.b.x) < ε && abs(e.a.y - e.b.y) < ε) { | |
e.a = e.b = null; | |
edges.splice(i, 1); | |
} | |
} | |
} | |
function d3_geom_voronoiConnectEdge(edge, extent) { | |
var vb = edge.b; | |
if (vb) return true; | |
var va = edge.a, x0 = extent[0][0], x1 = extent[1][0], y0 = extent[0][1], y1 = extent[1][1], lSite = edge.l, rSite = edge.r, lx = lSite.x, ly = lSite.y, rx = rSite.x, ry = rSite.y, fx = (lx + rx) / 2, fy = (ly + ry) / 2, fm, fb; | |
if (ry === ly) { | |
if (fx < x0 || fx >= x1) return; | |
if (lx > rx) { | |
if (!va) va = { | |
x: fx, | |
y: y0 | |
}; else if (va.y >= y1) return; | |
vb = { | |
x: fx, | |
y: y1 | |
}; | |
} else { | |
if (!va) va = { | |
x: fx, | |
y: y1 | |
}; else if (va.y < y0) return; | |
vb = { | |
x: fx, | |
y: y0 | |
}; | |
} | |
} else { | |
fm = (lx - rx) / (ry - ly); | |
fb = fy - fm * fx; | |
if (fm < -1 || fm > 1) { | |
if (lx > rx) { | |
if (!va) va = { | |
x: (y0 - fb) / fm, | |
y: y0 | |
}; else if (va.y >= y1) return; | |
vb = { | |
x: (y1 - fb) / fm, | |
y: y1 | |
}; | |
} else { | |
if (!va) va = { | |
x: (y1 - fb) / fm, | |
y: y1 | |
}; else if (va.y < y0) return; | |
vb = { | |
x: (y0 - fb) / fm, | |
y: y0 | |
}; | |
} | |
} else { | |
if (ly < ry) { | |
if (!va) va = { | |
x: x0, | |
y: fm * x0 + fb | |
}; else if (va.x >= x1) return; | |
vb = { | |
x: x1, | |
y: fm * x1 + fb | |
}; | |
} else { | |
if (!va) va = { | |
x: x1, | |
y: fm * x1 + fb | |
}; else if (va.x < x0) return; | |
vb = { | |
x: x0, | |
y: fm * x0 + fb | |
}; | |
} | |
} | |
} | |
edge.a = va; | |
edge.b = vb; | |
return true; | |
} | |
function d3_geom_voronoiEdge(lSite, rSite) { | |
this.l = lSite; | |
this.r = rSite; | |
this.a = this.b = null; | |
} | |
function d3_geom_voronoiCreateEdge(lSite, rSite, va, vb) { | |
var edge = new d3_geom_voronoiEdge(lSite, rSite); | |
d3_geom_voronoiEdges.push(edge); | |
if (va) d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, va); | |
if (vb) d3_geom_voronoiSetEdgeEnd(edge, rSite, lSite, vb); | |
d3_geom_voronoiCells[lSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, lSite, rSite)); | |
d3_geom_voronoiCells[rSite.i].edges.push(new d3_geom_voronoiHalfEdge(edge, rSite, lSite)); | |
return edge; | |
} | |
function d3_geom_voronoiCreateBorderEdge(lSite, va, vb) { | |
var edge = new d3_geom_voronoiEdge(lSite, null); | |
edge.a = va; | |
edge.b = vb; | |
d3_geom_voronoiEdges.push(edge); | |
return edge; | |
} | |
function d3_geom_voronoiSetEdgeEnd(edge, lSite, rSite, vertex) { | |
if (!edge.a && !edge.b) { | |
edge.a = vertex; | |
edge.l = lSite; | |
edge.r = rSite; | |
} else if (edge.l === rSite) { | |
edge.b = vertex; | |
} else { | |
edge.a = vertex; | |
} | |
} | |
function d3_geom_voronoiHalfEdge(edge, lSite, rSite) { | |
var va = edge.a, vb = edge.b; | |
this.edge = edge; | |
this.site = lSite; | |
this.angle = rSite ? Math.atan2(rSite.y - lSite.y, rSite.x - lSite.x) : edge.l === lSite ? Math.atan2(vb.x - va.x, va.y - vb.y) : Math.atan2(va.x - vb.x, vb.y - va.y); | |
} | |
d3_geom_voronoiHalfEdge.prototype = { | |
start: function() { | |
return this.edge.l === this.site ? this.edge.a : this.edge.b; | |
}, | |
end: function() { | |
return this.edge.l === this.site ? this.edge.b : this.edge.a; | |
} | |
}; | |
function d3_geom_voronoiRedBlackTree() { | |
this._ = null; | |
} | |
function d3_geom_voronoiRedBlackNode(node) { | |
node.U = node.C = node.L = node.R = node.P = node.N = null; | |
} | |
d3_geom_voronoiRedBlackTree.prototype = { | |
insert: function(after, node) { | |
var parent, grandpa, uncle; | |
if (after) { | |
node.P = after; | |
node.N = after.N; | |
if (after.N) after.N.P = node; | |
after.N = node; | |
if (after.R) { | |
after = after.R; | |
while (after.L) after = after.L; | |
after.L = node; | |
} else { | |
after.R = node; | |
} | |
parent = after; | |
} else if (this._) { | |
after = d3_geom_voronoiRedBlackFirst(this._); | |
node.P = null; | |
node.N = after; | |
after.P = after.L = node; | |
parent = after; | |
} else { | |
node.P = node.N = null; | |
this._ = node; | |
parent = null; | |
} | |
node.L = node.R = null; | |
node.U = parent; | |
node.C = true; | |
after = node; | |
while (parent && parent.C) { | |
grandpa = parent.U; | |
if (parent === grandpa.L) { | |
uncle = grandpa.R; | |
if (uncle && uncle.C) { | |
parent.C = uncle.C = false; | |
grandpa.C = true; | |
after = grandpa; | |
} else { | |
if (after === parent.R) { | |
d3_geom_voronoiRedBlackRotateLeft(this, parent); | |
after = parent; | |
parent = after.U; | |
} | |
parent.C = false; | |
grandpa.C = true; | |
d3_geom_voronoiRedBlackRotateRight(this, grandpa); | |
} | |
} else { | |
uncle = grandpa.L; | |
if (uncle && uncle.C) { | |
parent.C = uncle.C = false; | |
grandpa.C = true; | |
after = grandpa; | |
} else { | |
if (after === parent.L) { | |
d3_geom_voronoiRedBlackRotateRight(this, parent); | |
after = parent; | |
parent = after.U; | |
} | |
parent.C = false; | |
grandpa.C = true; | |
d3_geom_voronoiRedBlackRotateLeft(this, grandpa); | |
} | |
} | |
parent = after.U; | |
} | |
this._.C = false; | |
}, | |
remove: function(node) { | |
if (node.N) node.N.P = node.P; | |
if (node.P) node.P.N = node.N; | |
node.N = node.P = null; | |
var parent = node.U, sibling, left = node.L, right = node.R, next, red; | |
if (!left) next = right; else if (!right) next = left; else next = d3_geom_voronoiRedBlackFirst(right); | |
if (parent) { | |
if (parent.L === node) parent.L = next; else parent.R = next; | |
} else { | |
this._ = next; | |
} | |
if (left && right) { | |
red = next.C; | |
next.C = node.C; | |
next.L = left; | |
left.U = next; | |
if (next !== right) { | |
parent = next.U; | |
next.U = node.U; | |
node = next.R; | |
parent.L = node; | |
next.R = right; | |
right.U = next; | |
} else { | |
next.U = parent; | |
parent = next; | |
node = next.R; | |
} | |
} else { | |
red = node.C; | |
node = next; | |
} | |
if (node) node.U = parent; | |
if (red) return; | |
if (node && node.C) { | |
node.C = false; | |
return; | |
} | |
do { | |
if (node === this._) break; | |
if (node === parent.L) { | |
sibling = parent.R; | |
if (sibling.C) { | |
sibling.C = false; | |
parent.C = true; | |
d3_geom_voronoiRedBlackRotateLeft(this, parent); | |
sibling = parent.R; | |
} | |
if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { | |
if (!sibling.R || !sibling.R.C) { | |
sibling.L.C = false; | |
sibling.C = true; | |
d3_geom_voronoiRedBlackRotateRight(this, sibling); | |
sibling = parent.R; | |
} | |
sibling.C = parent.C; | |
parent.C = sibling.R.C = false; | |
d3_geom_voronoiRedBlackRotateLeft(this, parent); | |
node = this._; | |
break; | |
} | |
} else { | |
sibling = parent.L; | |
if (sibling.C) { | |
sibling.C = false; | |
parent.C = true; | |
d3_geom_voronoiRedBlackRotateRight(this, parent); | |
sibling = parent.L; | |
} | |
if (sibling.L && sibling.L.C || sibling.R && sibling.R.C) { | |
if (!sibling.L || !sibling.L.C) { | |
sibling.R.C = false; | |
sibling.C = true; | |
d3_geom_voronoiRedBlackRotateLeft(this, sibling); | |
sibling = parent.L; | |
} | |
sibling.C = parent.C; | |
parent.C = sibling.L.C = false; | |
d3_geom_voronoiRedBlackRotateRight(this, parent); | |
node = this._; | |
break; | |
} | |
} | |
sibling.C = true; | |
node = parent; | |
parent = parent.U; | |
} while (!node.C); | |
if (node) node.C = false; | |
} | |
}; | |
function d3_geom_voronoiRedBlackRotateLeft(tree, node) { | |
var p = node, q = node.R, parent = p.U; | |
if (parent) { | |
if (parent.L === p) parent.L = q; else parent.R = q; | |
} else { | |
tree._ = q; | |
} | |
q.U = parent; | |
p.U = q; | |
p.R = q.L; | |
if (p.R) p.R.U = p; | |
q.L = p; | |
} | |
function d3_geom_voronoiRedBlackRotateRight(tree, node) { | |
var p = node, q = node.L, parent = p.U; | |
if (parent) { | |
if (parent.L === p) parent.L = q; else parent.R = q; | |
} else { | |
tree._ = q; | |
} | |
q.U = parent; | |
p.U = q; | |
p.L = q.R; | |
if (p.L) p.L.U = p; | |
q.R = p; | |
} | |
function d3_geom_voronoiRedBlackFirst(node) { | |
while (node.L) node = node.L; | |
return node; | |
} | |
function d3_geom_voronoi(sites, bbox) { | |
var site = sites.sort(d3_geom_voronoiVertexOrder).pop(), x0, y0, circle; | |
d3_geom_voronoiEdges = []; | |
d3_geom_voronoiCells = new Array(sites.length); | |
d3_geom_voronoiBeaches = new d3_geom_voronoiRedBlackTree(); | |
d3_geom_voronoiCircles = new d3_geom_voronoiRedBlackTree(); | |
while (true) { | |
circle = d3_geom_voronoiFirstCircle; | |
if (site && (!circle || site.y < circle.y || site.y === circle.y && site.x < circle.x)) { | |
if (site.x !== x0 || site.y !== y0) { | |
d3_geom_voronoiCells[site.i] = new d3_geom_voronoiCell(site); | |
d3_geom_voronoiAddBeach(site); | |
x0 = site.x, y0 = site.y; | |
} | |
site = sites.pop(); | |
} else if (circle) { | |
d3_geom_voronoiRemoveBeach(circle.arc); | |
} else { | |
break; | |
} | |
} | |
if (bbox) d3_geom_voronoiClipEdges(bbox), d3_geom_voronoiCloseCells(bbox); | |
var diagram = { | |
cells: d3_geom_voronoiCells, | |
edges: d3_geom_voronoiEdges | |
}; | |
d3_geom_voronoiBeaches = d3_geom_voronoiCircles = d3_geom_voronoiEdges = d3_geom_voronoiCells = null; | |
return diagram; | |
} | |
function d3_geom_voronoiVertexOrder(a, b) { | |
return b.y - a.y || b.x - a.x; | |
} | |
d3.geom.voronoi = function(points) { | |
var x = d3_geom_pointX, y = d3_geom_pointY, fx = x, fy = y, clipExtent = d3_geom_voronoiClipExtent; | |
if (points) return voronoi(points); | |
function voronoi(data) { | |
var polygons = new Array(data.length), x0 = clipExtent[0][0], y0 = clipExtent[0][1], x1 = clipExtent[1][0], y1 = clipExtent[1][1]; | |
d3_geom_voronoi(sites(data), clipExtent).cells.forEach(function(cell, i) { | |
var edges = cell.edges, site = cell.site, polygon = polygons[i] = edges.length ? edges.map(function(e) { | |
var s = e.start(); | |
return [ s.x, s.y ]; | |
}) : site.x >= x0 && site.x <= x1 && site.y >= y0 && site.y <= y1 ? [ [ x0, y1 ], [ x1, y1 ], [ x1, y0 ], [ x0, y0 ] ] : []; | |
polygon.point = data[i]; | |
}); | |
return polygons; | |
} | |
function sites(data) { | |
return data.map(function(d, i) { | |
return { | |
x: Math.round(fx(d, i) / ε) * ε, | |
y: Math.round(fy(d, i) / ε) * ε, | |
i: i | |
}; | |
}); | |
} | |
voronoi.links = function(data) { | |
return d3_geom_voronoi(sites(data)).edges.filter(function(edge) { | |
return edge.l && edge.r; | |
}).map(function(edge) { | |
return { | |
source: data[edge.l.i], | |
target: data[edge.r.i] | |
}; | |
}); | |
}; | |
voronoi.triangles = function(data) { | |
var triangles = []; | |
d3_geom_voronoi(sites(data)).cells.forEach(function(cell, i) { | |
var site = cell.site, edges = cell.edges.sort(d3_geom_voronoiHalfEdgeOrder), j = -1, m = edges.length, e0, s0, e1 = edges[m - 1].edge, s1 = e1.l === site ? e1.r : e1.l; | |
while (++j < m) { | |
e0 = e1; | |
s0 = s1; | |
e1 = edges[j].edge; | |
s1 = e1.l === site ? e1.r : e1.l; | |
if (i < s0.i && i < s1.i && d3_geom_voronoiTriangleArea(site, s0, s1) < 0) { | |
triangles.push([ data[i], data[s0.i], data[s1.i] ]); | |
} | |
} | |
}); | |
return triangles; | |
}; | |
voronoi.x = function(_) { | |
return arguments.length ? (fx = d3_functor(x = _), voronoi) : x; | |
}; | |
voronoi.y = function(_) { | |
return arguments.length ? (fy = d3_functor(y = _), voronoi) : y; | |
}; | |
voronoi.clipExtent = function(_) { | |
if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent; | |
clipExtent = _ == null ? d3_geom_voronoiClipExtent : _; | |
return voronoi; | |
}; | |
voronoi.size = function(_) { | |
if (!arguments.length) return clipExtent === d3_geom_voronoiClipExtent ? null : clipExtent && clipExtent[1]; | |
return voronoi.clipExtent(_ && [ [ 0, 0 ], _ ]); | |
}; | |
return voronoi; | |
}; | |
var d3_geom_voronoiClipExtent = [ [ -1e6, -1e6 ], [ 1e6, 1e6 ] ]; | |
function d3_geom_voronoiTriangleArea(a, b, c) { | |
return (a.x - c.x) * (b.y - a.y) - (a.x - b.x) * (c.y - a.y); | |
} | |
d3.geom.delaunay = function(vertices) { | |
return d3.geom.voronoi().triangles(vertices); | |
}; | |
d3.geom.quadtree = function(points, x1, y1, x2, y2) { | |
var x = d3_geom_pointX, y = d3_geom_pointY, compat; | |
if (compat = arguments.length) { | |
x = d3_geom_quadtreeCompatX; | |
y = d3_geom_quadtreeCompatY; | |
if (compat === 3) { | |
y2 = y1; | |
x2 = x1; | |
y1 = x1 = 0; | |
} | |
return quadtree(points); | |
} | |
function quadtree(data) { | |
var d, fx = d3_functor(x), fy = d3_functor(y), xs, ys, i, n, x1_, y1_, x2_, y2_; | |
if (x1 != null) { | |
x1_ = x1, y1_ = y1, x2_ = x2, y2_ = y2; | |
} else { | |
x2_ = y2_ = -(x1_ = y1_ = Infinity); | |
xs = [], ys = []; | |
n = data.length; | |
if (compat) for (i = 0; i < n; ++i) { | |
d = data[i]; | |
if (d.x < x1_) x1_ = d.x; | |
if (d.y < y1_) y1_ = d.y; | |
if (d.x > x2_) x2_ = d.x; | |
if (d.y > y2_) y2_ = d.y; | |
xs.push(d.x); | |
ys.push(d.y); | |
} else for (i = 0; i < n; ++i) { | |
var x_ = +fx(d = data[i], i), y_ = +fy(d, i); | |
if (x_ < x1_) x1_ = x_; | |
if (y_ < y1_) y1_ = y_; | |
if (x_ > x2_) x2_ = x_; | |
if (y_ > y2_) y2_ = y_; | |
xs.push(x_); | |
ys.push(y_); | |
} | |
} | |
var dx = x2_ - x1_, dy = y2_ - y1_; | |
if (dx > dy) y2_ = y1_ + dx; else x2_ = x1_ + dy; | |
function insert(n, d, x, y, x1, y1, x2, y2) { | |
if (isNaN(x) || isNaN(y)) return; | |
if (n.leaf) { | |
var nx = n.x, ny = n.y; | |
if (nx != null) { | |
if (abs(nx - x) + abs(ny - y) < .01) { | |
insertChild(n, d, x, y, x1, y1, x2, y2); | |
} else { | |
var nPoint = n.point; | |
n.x = n.y = n.point = null; | |
insertChild(n, nPoint, nx, ny, x1, y1, x2, y2); | |
insertChild(n, d, x, y, x1, y1, x2, y2); | |
} | |
} else { | |
n.x = x, n.y = y, n.point = d; | |
} | |
} else { | |
insertChild(n, d, x, y, x1, y1, x2, y2); | |
} | |
} | |
function insertChild(n, d, x, y, x1, y1, x2, y2) { | |
var xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym, i = below << 1 | right; | |
n.leaf = false; | |
n = n.nodes[i] || (n.nodes[i] = d3_geom_quadtreeNode()); | |
if (right) x1 = xm; else x2 = xm; | |
if (below) y1 = ym; else y2 = ym; | |
insert(n, d, x, y, x1, y1, x2, y2); | |
} | |
var root = d3_geom_quadtreeNode(); | |
root.add = function(d) { | |
insert(root, d, +fx(d, ++i), +fy(d, i), x1_, y1_, x2_, y2_); | |
}; | |
root.visit = function(f) { | |
d3_geom_quadtreeVisit(f, root, x1_, y1_, x2_, y2_); | |
}; | |
root.find = function(point) { | |
return d3_geom_quadtreeFind(root, point[0], point[1], x1_, y1_, x2_, y2_); | |
}; | |
i = -1; | |
if (x1 == null) { | |
while (++i < n) { | |
insert(root, data[i], xs[i], ys[i], x1_, y1_, x2_, y2_); | |
} | |
--i; | |
} else data.forEach(root.add); | |
xs = ys = data = d = null; | |
return root; | |
} | |
quadtree.x = function(_) { | |
return arguments.length ? (x = _, quadtree) : x; | |
}; | |
quadtree.y = function(_) { | |
return arguments.length ? (y = _, quadtree) : y; | |
}; | |
quadtree.extent = function(_) { | |
if (!arguments.length) return x1 == null ? null : [ [ x1, y1 ], [ x2, y2 ] ]; | |
if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = +_[0][0], y1 = +_[0][1], x2 = +_[1][0], | |
y2 = +_[1][1]; | |
return quadtree; | |
}; | |
quadtree.size = function(_) { | |
if (!arguments.length) return x1 == null ? null : [ x2 - x1, y2 - y1 ]; | |
if (_ == null) x1 = y1 = x2 = y2 = null; else x1 = y1 = 0, x2 = +_[0], y2 = +_[1]; | |
return quadtree; | |
}; | |
return quadtree; | |
}; | |
function d3_geom_quadtreeCompatX(d) { | |
return d.x; | |
} | |
function d3_geom_quadtreeCompatY(d) { | |
return d.y; | |
} | |
function d3_geom_quadtreeNode() { | |
return { | |
leaf: true, | |
nodes: [], | |
point: null, | |
x: null, | |
y: null | |
}; | |
} | |
function d3_geom_quadtreeVisit(f, node, x1, y1, x2, y2) { | |
if (!f(node, x1, y1, x2, y2)) { | |
var sx = (x1 + x2) * .5, sy = (y1 + y2) * .5, children = node.nodes; | |
if (children[0]) d3_geom_quadtreeVisit(f, children[0], x1, y1, sx, sy); | |
if (children[1]) d3_geom_quadtreeVisit(f, children[1], sx, y1, x2, sy); | |
if (children[2]) d3_geom_quadtreeVisit(f, children[2], x1, sy, sx, y2); | |
if (children[3]) d3_geom_quadtreeVisit(f, children[3], sx, sy, x2, y2); | |
} | |
} | |
function d3_geom_quadtreeFind(root, x, y, x0, y0, x3, y3) { | |
var minDistance2 = Infinity, closestPoint; | |
(function find(node, x1, y1, x2, y2) { | |
if (x1 > x3 || y1 > y3 || x2 < x0 || y2 < y0) return; | |
if (point = node.point) { | |
var point, dx = x - node.x, dy = y - node.y, distance2 = dx * dx + dy * dy; | |
if (distance2 < minDistance2) { | |
var distance = Math.sqrt(minDistance2 = distance2); | |
x0 = x - distance, y0 = y - distance; | |
x3 = x + distance, y3 = y + distance; | |
closestPoint = point; | |
} | |
} | |
var children = node.nodes, xm = (x1 + x2) * .5, ym = (y1 + y2) * .5, right = x >= xm, below = y >= ym; | |
for (var i = below << 1 | right, j = i + 4; i < j; ++i) { | |
if (node = children[i & 3]) switch (i & 3) { | |
case 0: | |
find(node, x1, y1, xm, ym); | |
break; | |
case 1: | |
find(node, xm, y1, x2, ym); | |
break; | |
case 2: | |
find(node, x1, ym, xm, y2); | |
break; | |
case 3: | |
find(node, xm, ym, x2, y2); | |
break; | |
} | |
} | |
})(root, x0, y0, x3, y3); | |
return closestPoint; | |
} | |
d3.interpolateRgb = d3_interpolateRgb; | |
function d3_interpolateRgb(a, b) { | |
a = d3.rgb(a); | |
b = d3.rgb(b); | |
var ar = a.r, ag = a.g, ab = a.b, br = b.r - ar, bg = b.g - ag, bb = b.b - ab; | |
return function(t) { | |
return "#" + d3_rgb_hex(Math.round(ar + br * t)) + d3_rgb_hex(Math.round(ag + bg * t)) + d3_rgb_hex(Math.round(ab + bb * t)); | |
}; | |
} | |
d3.interpolateObject = d3_interpolateObject; | |
function d3_interpolateObject(a, b) { | |
var i = {}, c = {}, k; | |
for (k in a) { | |
if (k in b) { | |
i[k] = d3_interpolate(a[k], b[k]); | |
} else { | |
c[k] = a[k]; | |
} | |
} | |
for (k in b) { | |
if (!(k in a)) { | |
c[k] = b[k]; | |
} | |
} | |
return function(t) { | |
for (k in i) c[k] = i[k](t); | |
return c; | |
}; | |
} | |
d3.interpolateNumber = d3_interpolateNumber; | |
function d3_interpolateNumber(a, b) { | |
a = +a, b = +b; | |
return function(t) { | |
return a * (1 - t) + b * t; | |
}; | |
} | |
d3.interpolateString = d3_interpolateString; | |
function d3_interpolateString(a, b) { | |
var bi = d3_interpolate_numberA.lastIndex = d3_interpolate_numberB.lastIndex = 0, am, bm, bs, i = -1, s = [], q = []; | |
a = a + "", b = b + ""; | |
while ((am = d3_interpolate_numberA.exec(a)) && (bm = d3_interpolate_numberB.exec(b))) { | |
if ((bs = bm.index) > bi) { | |
bs = b.slice(bi, bs); | |
if (s[i]) s[i] += bs; else s[++i] = bs; | |
} | |
if ((am = am[0]) === (bm = bm[0])) { | |
if (s[i]) s[i] += bm; else s[++i] = bm; | |
} else { | |
s[++i] = null; | |
q.push({ | |
i: i, | |
x: d3_interpolateNumber(am, bm) | |
}); | |
} | |
bi = d3_interpolate_numberB.lastIndex; | |
} | |
if (bi < b.length) { | |
bs = b.slice(bi); | |
if (s[i]) s[i] += bs; else s[++i] = bs; | |
} | |
return s.length < 2 ? q[0] ? (b = q[0].x, function(t) { | |
return b(t) + ""; | |
}) : function() { | |
return b; | |
} : (b = q.length, function(t) { | |
for (var i = 0, o; i < b; ++i) s[(o = q[i]).i] = o.x(t); | |
return s.join(""); | |
}); | |
} | |
var d3_interpolate_numberA = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, d3_interpolate_numberB = new RegExp(d3_interpolate_numberA.source, "g"); | |
d3.interpolate = d3_interpolate; | |
function d3_interpolate(a, b) { | |
var i = d3.interpolators.length, f; | |
while (--i >= 0 && !(f = d3.interpolators[i](a, b))) ; | |
return f; | |
} | |
d3.interpolators = [ function(a, b) { | |
var t = typeof b; | |
return (t === "string" ? d3_rgb_names.has(b) || /^(#|rgb\(|hsl\()/.test(b) ? d3_interpolateRgb : d3_interpolateString : b instanceof d3_color ? d3_interpolateRgb : Array.isArray(b) ? d3_interpolateArray : t === "object" && isNaN(b) ? d3_interpolateObject : d3_interpolateNumber)(a, b); | |
} ]; | |
d3.interpolateArray = d3_interpolateArray; | |
function d3_interpolateArray(a, b) { | |
var x = [], c = [], na = a.length, nb = b.length, n0 = Math.min(a.length, b.length), i; | |
for (i = 0; i < n0; ++i) x.push(d3_interpolate(a[i], b[i])); | |
for (;i < na; ++i) c[i] = a[i]; | |
for (;i < nb; ++i) c[i] = b[i]; | |
return function(t) { | |
for (i = 0; i < n0; ++i) c[i] = x[i](t); | |
return c; | |
}; | |
} | |
var d3_ease_default = function() { | |
return d3_identity; | |
}; | |
var d3_ease = d3.map({ | |
linear: d3_ease_default, | |
poly: d3_ease_poly, | |
quad: function() { | |
return d3_ease_quad; | |
}, | |
cubic: function() { | |
return d3_ease_cubic; | |
}, | |
sin: function() { | |
return d3_ease_sin; | |
}, | |
exp: function() { | |
return d3_ease_exp; | |
}, | |
circle: function() { | |
return d3_ease_circle; | |
}, | |
elastic: d3_ease_elastic, | |
back: d3_ease_back, | |
bounce: function() { | |
return d3_ease_bounce; | |
} | |
}); | |
var d3_ease_mode = d3.map({ | |
"in": d3_identity, | |
out: d3_ease_reverse, | |
"in-out": d3_ease_reflect, | |
"out-in": function(f) { | |
return d3_ease_reflect(d3_ease_reverse(f)); | |
} | |
}); | |
d3.ease = function(name) { | |
var i = name.indexOf("-"), t = i >= 0 ? name.slice(0, i) : name, m = i >= 0 ? name.slice(i + 1) : "in"; | |
t = d3_ease.get(t) || d3_ease_default; | |
m = d3_ease_mode.get(m) || d3_identity; | |
return d3_ease_clamp(m(t.apply(null, d3_arraySlice.call(arguments, 1)))); | |
}; | |
function d3_ease_clamp(f) { | |
return function(t) { | |
return t <= 0 ? 0 : t >= 1 ? 1 : f(t); | |
}; | |
} | |
function d3_ease_reverse(f) { | |
return function(t) { | |
return 1 - f(1 - t); | |
}; | |
} | |
function d3_ease_reflect(f) { | |
return function(t) { | |
return .5 * (t < .5 ? f(2 * t) : 2 - f(2 - 2 * t)); | |
}; | |
} | |
function d3_ease_quad(t) { | |
return t * t; | |
} | |
function d3_ease_cubic(t) { | |
return t * t * t; | |
} | |
function d3_ease_cubicInOut(t) { | |
if (t <= 0) return 0; | |
if (t >= 1) return 1; | |
var t2 = t * t, t3 = t2 * t; | |
return 4 * (t < .5 ? t3 : 3 * (t - t2) + t3 - .75); | |
} | |
function d3_ease_poly(e) { | |
return function(t) { | |
return Math.pow(t, e); | |
}; | |
} | |
function d3_ease_sin(t) { | |
return 1 - Math.cos(t * halfπ); | |
} | |
function d3_ease_exp(t) { | |
return Math.pow(2, 10 * (t - 1)); | |
} | |
function d3_ease_circle(t) { | |
return 1 - Math.sqrt(1 - t * t); | |
} | |
function d3_ease_elastic(a, p) { | |
var s; | |
if (arguments.length < 2) p = .45; | |
if (arguments.length) s = p / τ * Math.asin(1 / a); else a = 1, s = p / 4; | |
return function(t) { | |
return 1 + a * Math.pow(2, -10 * t) * Math.sin((t - s) * τ / p); | |
}; | |
} | |
function d3_ease_back(s) { | |
if (!s) s = 1.70158; | |
return function(t) { | |
return t * t * ((s + 1) * t - s); | |
}; | |
} | |
function d3_ease_bounce(t) { | |
return t < 1 / 2.75 ? 7.5625 * t * t : t < 2 / 2.75 ? 7.5625 * (t -= 1.5 / 2.75) * t + .75 : t < 2.5 / 2.75 ? 7.5625 * (t -= 2.25 / 2.75) * t + .9375 : 7.5625 * (t -= 2.625 / 2.75) * t + .984375; | |
} | |
d3.interpolateHcl = d3_interpolateHcl; | |
function d3_interpolateHcl(a, b) { | |
a = d3.hcl(a); | |
b = d3.hcl(b); | |
var ah = a.h, ac = a.c, al = a.l, bh = b.h - ah, bc = b.c - ac, bl = b.l - al; | |
if (isNaN(bc)) bc = 0, ac = isNaN(ac) ? b.c : ac; | |
if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; | |
return function(t) { | |
return d3_hcl_lab(ah + bh * t, ac + bc * t, al + bl * t) + ""; | |
}; | |
} | |
d3.interpolateHsl = d3_interpolateHsl; | |
function d3_interpolateHsl(a, b) { | |
a = d3.hsl(a); | |
b = d3.hsl(b); | |
var ah = a.h, as = a.s, al = a.l, bh = b.h - ah, bs = b.s - as, bl = b.l - al; | |
if (isNaN(bs)) bs = 0, as = isNaN(as) ? b.s : as; | |
if (isNaN(bh)) bh = 0, ah = isNaN(ah) ? b.h : ah; else if (bh > 180) bh -= 360; else if (bh < -180) bh += 360; | |
return function(t) { | |
return d3_hsl_rgb(ah + bh * t, as + bs * t, al + bl * t) + ""; | |
}; | |
} | |
d3.interpolateLab = d3_interpolateLab; | |
function d3_interpolateLab(a, b) { | |
a = d3.lab(a); | |
b = d3.lab(b); | |
var al = a.l, aa = a.a, ab = a.b, bl = b.l - al, ba = b.a - aa, bb = b.b - ab; | |
return function(t) { | |
return d3_lab_rgb(al + bl * t, aa + ba * t, ab + bb * t) + ""; | |
}; | |
} | |
d3.interpolateRound = d3_interpolateRound; | |
function d3_interpolateRound(a, b) { | |
b -= a; | |
return function(t) { | |
return Math.round(a + b * t); | |
}; | |
} | |
d3.transform = function(string) { | |
var g = d3_document.createElementNS(d3.ns.prefix.svg, "g"); | |
return (d3.transform = function(string) { | |
if (string != null) { | |
g.setAttribute("transform", string); | |
var t = g.transform.baseVal.consolidate(); | |
} | |
return new d3_transform(t ? t.matrix : d3_transformIdentity); | |
})(string); | |
}; | |
function d3_transform(m) { | |
var r0 = [ m.a, m.b ], r1 = [ m.c, m.d ], kx = d3_transformNormalize(r0), kz = d3_transformDot(r0, r1), ky = d3_transformNormalize(d3_transformCombine(r1, r0, -kz)) || 0; | |
if (r0[0] * r1[1] < r1[0] * r0[1]) { | |
r0[0] *= -1; | |
r0[1] *= -1; | |
kx *= -1; | |
kz *= -1; | |
} | |
this.rotate = (kx ? Math.atan2(r0[1], r0[0]) : Math.atan2(-r1[0], r1[1])) * d3_degrees; | |
this.translate = [ m.e, m.f ]; | |
this.scale = [ kx, ky ]; | |
this.skew = ky ? Math.atan2(kz, ky) * d3_degrees : 0; | |
} | |
d3_transform.prototype.toString = function() { | |
return "translate(" + this.translate + ")rotate(" + this.rotate + ")skewX(" + this.skew + ")scale(" + this.scale + ")"; | |
}; | |
function d3_transformDot(a, b) { | |
return a[0] * b[0] + a[1] * b[1]; | |
} | |
function d3_transformNormalize(a) { | |
var k = Math.sqrt(d3_transformDot(a, a)); | |
if (k) { | |
a[0] /= k; | |
a[1] /= k; | |
} | |
return k; | |
} | |
function d3_transformCombine(a, b, k) { | |
a[0] += k * b[0]; | |
a[1] += k * b[1]; | |
return a; | |
} | |
var d3_transformIdentity = { | |
a: 1, | |
b: 0, | |
c: 0, | |
d: 1, | |
e: 0, | |
f: 0 | |
}; | |
d3.interpolateTransform = d3_interpolateTransform; | |
function d3_interpolateTransform(a, b) { | |
var s = [], q = [], n, A = d3.transform(a), B = d3.transform(b), ta = A.translate, tb = B.translate, ra = A.rotate, rb = B.rotate, wa = A.skew, wb = B.skew, ka = A.scale, kb = B.scale; | |
if (ta[0] != tb[0] || ta[1] != tb[1]) { | |
s.push("translate(", null, ",", null, ")"); | |
q.push({ | |
i: 1, | |
x: d3_interpolateNumber(ta[0], tb[0]) | |
}, { | |
i: 3, | |
x: d3_interpolateNumber(ta[1], tb[1]) | |
}); | |
} else if (tb[0] || tb[1]) { | |
s.push("translate(" + tb + ")"); | |
} else { | |
s.push(""); | |
} | |
if (ra != rb) { | |
if (ra - rb > 180) rb += 360; else if (rb - ra > 180) ra += 360; | |
q.push({ | |
i: s.push(s.pop() + "rotate(", null, ")") - 2, | |
x: d3_interpolateNumber(ra, rb) | |
}); | |
} else if (rb) { | |
s.push(s.pop() + "rotate(" + rb + ")"); | |
} | |
if (wa != wb) { | |
q.push({ | |
i: s.push(s.pop() + "skewX(", null, ")") - 2, | |
x: d3_interpolateNumber(wa, wb) | |
}); | |
} else if (wb) { | |
s.push(s.pop() + "skewX(" + wb + ")"); | |
} | |
if (ka[0] != kb[0] || ka[1] != kb[1]) { | |
n = s.push(s.pop() + "scale(", null, ",", null, ")"); | |
q.push({ | |
i: n - 4, | |
x: d3_interpolateNumber(ka[0], kb[0]) | |
}, { | |
i: n - 2, | |
x: d3_interpolateNumber(ka[1], kb[1]) | |
}); | |
} else if (kb[0] != 1 || kb[1] != 1) { | |
s.push(s.pop() + "scale(" + kb + ")"); | |
} | |
n = q.length; | |
return function(t) { | |
var i = -1, o; | |
while (++i < n) s[(o = q[i]).i] = o.x(t); | |
return s.join(""); | |
}; | |
} | |
function d3_uninterpolateNumber(a, b) { | |
b = (b -= a = +a) || 1 / b; | |
return function(x) { | |
return (x - a) / b; | |
}; | |
} | |
function d3_uninterpolateClamp(a, b) { | |
b = (b -= a = +a) || 1 / b; | |
return function(x) { | |
return Math.max(0, Math.min(1, (x - a) / b)); | |
}; | |
} | |
d3.layout = {}; | |
d3.layout.bundle = function() { | |
return function(links) { | |
var paths = [], i = -1, n = links.length; | |
while (++i < n) paths.push(d3_layout_bundlePath(links[i])); | |
return paths; | |
}; | |
}; | |
function d3_layout_bundlePath(link) { | |
var start = link.source, end = link.target, lca = d3_layout_bundleLeastCommonAncestor(start, end), points = [ start ]; | |
while (start !== lca) { | |
start = start.parent; | |
points.push(start); | |
} | |
var k = points.length; | |
while (end !== lca) { | |
points.splice(k, 0, end); | |
end = end.parent; | |
} | |
return points; | |
} | |
function d3_layout_bundleAncestors(node) { | |
var ancestors = [], parent = node.parent; | |
while (parent != null) { | |
ancestors.push(node); | |
node = parent; | |
parent = parent.parent; | |
} | |
ancestors.push(node); | |
return ancestors; | |
} | |
function d3_layout_bundleLeastCommonAncestor(a, b) { | |
if (a === b) return a; | |
var aNodes = d3_layout_bundleAncestors(a), bNodes = d3_layout_bundleAncestors(b), aNode = aNodes.pop(), bNode = bNodes.pop(), sharedNode = null; | |
while (aNode === bNode) { | |
sharedNode = aNode; | |
aNode = aNodes.pop(); | |
bNode = bNodes.pop(); | |
} | |
return sharedNode; | |
} | |
d3.layout.chord = function() { | |
var chord = {}, chords, groups, matrix, n, padding = 0, sortGroups, sortSubgroups, sortChords; | |
function relayout() { | |
var subgroups = {}, groupSums = [], groupIndex = d3.range(n), subgroupIndex = [], k, x, x0, i, j; | |
chords = []; | |
groups = []; | |
k = 0, i = -1; | |
while (++i < n) { | |
x = 0, j = -1; | |
while (++j < n) { | |
x += matrix[i][j]; | |
} | |
groupSums.push(x); | |
subgroupIndex.push(d3.range(n)); | |
k += x; | |
} | |
if (sortGroups) { | |
groupIndex.sort(function(a, b) { | |
return sortGroups(groupSums[a], groupSums[b]); | |
}); | |
} | |
if (sortSubgroups) { | |
subgroupIndex.forEach(function(d, i) { | |
d.sort(function(a, b) { | |
return sortSubgroups(matrix[i][a], matrix[i][b]); | |
}); | |
}); | |
} | |
k = (τ - padding * n) / k; | |
x = 0, i = -1; | |
while (++i < n) { | |
x0 = x, j = -1; | |
while (++j < n) { | |
var di = groupIndex[i], dj = subgroupIndex[di][j], v = matrix[di][dj], a0 = x, a1 = x += v * k; | |
subgroups[di + "-" + dj] = { | |
index: di, | |
subindex: dj, | |
startAngle: a0, | |
endAngle: a1, | |
value: v | |
}; | |
} | |
groups[di] = { | |
index: di, | |
startAngle: x0, | |
endAngle: x, | |
value: (x - x0) / k | |
}; | |
x += padding; | |
} | |
i = -1; | |
while (++i < n) { | |
j = i - 1; | |
while (++j < n) { | |
var source = subgroups[i + "-" + j], target = subgroups[j + "-" + i]; | |
if (source.value || target.value) { | |
chords.push(source.value < target.value ? { | |
source: target, | |
target: source | |
} : { | |
source: source, | |
target: target | |
}); | |
} | |
} | |
} | |
if (sortChords) resort(); | |
} | |
function resort() { | |
chords.sort(function(a, b) { | |
return sortChords((a.source.value + a.target.value) / 2, (b.source.value + b.target.value) / 2); | |
}); | |
} | |
chord.matrix = function(x) { | |
if (!arguments.length) return matrix; | |
n = (matrix = x) && matrix.length; | |
chords = groups = null; | |
return chord; | |
}; | |
chord.padding = function(x) { | |
if (!arguments.length) return padding; | |
padding = x; | |
chords = groups = null; | |
return chord; | |
}; | |
chord.sortGroups = function(x) { | |
if (!arguments.length) return sortGroups; | |
sortGroups = x; | |
chords = groups = null; | |
return chord; | |
}; | |
chord.sortSubgroups = function(x) { | |
if (!arguments.length) return sortSubgroups; | |
sortSubgroups = x; | |
chords = null; | |
return chord; | |
}; | |
chord.sortChords = function(x) { | |
if (!arguments.length) return sortChords; | |
sortChords = x; | |
if (chords) resort(); | |
return chord; | |
}; | |
chord.chords = function() { | |
if (!chords) relayout(); | |
return chords; | |
}; | |
chord.groups = function() { | |
if (!groups) relayout(); | |
return groups; | |
}; | |
return chord; | |
}; | |
d3.layout.force = function() { | |
var force = {}, event = d3.dispatch("start", "tick", "end"), size = [ 1, 1 ], drag, alpha, friction = .9, linkDistance = d3_layout_forceLinkDistance, linkStrength = d3_layout_forceLinkStrength, charge = -30, chargeDistance2 = d3_layout_forceChargeDistance2, gravity = .1, theta2 = .64, nodes = [], links = [], distances, strengths, charges; | |
function repulse(node) { | |
return function(quad, x1, _, x2) { | |
if (quad.point !== node) { | |
var dx = quad.cx - node.x, dy = quad.cy - node.y, dw = x2 - x1, dn = dx * dx + dy * dy; | |
if (dw * dw / theta2 < dn) { | |
if (dn < chargeDistance2) { | |
var k = quad.charge / dn; | |
node.px -= dx * k; | |
node.py -= dy * k; | |
} | |
return true; | |
} | |
if (quad.point && dn && dn < chargeDistance2) { | |
var k = quad.pointCharge / dn; | |
node.px -= dx * k; | |
node.py -= dy * k; | |
} | |
} | |
return !quad.charge; | |
}; | |
} | |
force.tick = function() { | |
if ((alpha *= .99) < .005) { | |
event.end({ | |
type: "end", | |
alpha: alpha = 0 | |
}); | |
return true; | |
} | |
var n = nodes.length, m = links.length, q, i, o, s, t, l, k, x, y; | |
for (i = 0; i < m; ++i) { | |
o = links[i]; | |
s = o.source; | |
t = o.target; | |
x = t.x - s.x; | |
y = t.y - s.y; | |
if (l = x * x + y * y) { | |
l = alpha * strengths[i] * ((l = Math.sqrt(l)) - distances[i]) / l; | |
x *= l; | |
y *= l; | |
t.x -= x * (k = s.weight / (t.weight + s.weight)); | |
t.y -= y * k; | |
s.x += x * (k = 1 - k); | |
s.y += y * k; | |
} | |
} | |
if (k = alpha * gravity) { | |
x = size[0] / 2; | |
y = size[1] / 2; | |
i = -1; | |
if (k) while (++i < n) { | |
o = nodes[i]; | |
o.x += (x - o.x) * k; | |
o.y += (y - o.y) * k; | |
} | |
} | |
if (charge) { | |
d3_layout_forceAccumulate(q = d3.geom.quadtree(nodes), alpha, charges); | |
i = -1; | |
while (++i < n) { | |
if (!(o = nodes[i]).fixed) { | |
q.visit(repulse(o)); | |
} | |
} | |
} | |
i = -1; | |
while (++i < n) { | |
o = nodes[i]; | |
if (o.fixed) { | |
o.x = o.px; | |
o.y = o.py; | |
} else { | |
o.x -= (o.px - (o.px = o.x)) * friction; | |
o.y -= (o.py - (o.py = o.y)) * friction; | |
} | |
} | |
event.tick({ | |
type: "tick", | |
alpha: alpha | |
}); | |
}; | |
force.nodes = function(x) { | |
if (!arguments.length) return nodes; | |
nodes = x; | |
return force; | |
}; | |
force.links = function(x) { | |
if (!arguments.length) return links; | |
links = x; | |
return force; | |
}; | |
force.size = function(x) { | |
if (!arguments.length) return size; | |
size = x; | |
return force; | |
}; | |
force.linkDistance = function(x) { | |
if (!arguments.length) return linkDistance; | |
linkDistance = typeof x === "function" ? x : +x; | |
return force; | |
}; | |
force.distance = force.linkDistance; | |
force.linkStrength = function(x) { | |
if (!arguments.length) return linkStrength; | |
linkStrength = typeof x === "function" ? x : +x; | |
return force; | |
}; | |
force.friction = function(x) { | |
if (!arguments.length) return friction; | |
friction = +x; | |
return force; | |
}; | |
force.charge = function(x) { | |
if (!arguments.length) return charge; | |
charge = typeof x === "function" ? x : +x; | |
return force; | |
}; | |
force.chargeDistance = function(x) { | |
if (!arguments.length) return Math.sqrt(chargeDistance2); | |
chargeDistance2 = x * x; | |
return force; | |
}; | |
force.gravity = function(x) { | |
if (!arguments.length) return gravity; | |
gravity = +x; | |
return force; | |
}; | |
force.theta = function(x) { | |
if (!arguments.length) return Math.sqrt(theta2); | |
theta2 = x * x; | |
return force; | |
}; | |
force.alpha = function(x) { | |
if (!arguments.length) return alpha; | |
x = +x; | |
if (alpha) { | |
if (x > 0) alpha = x; else alpha = 0; | |
} else if (x > 0) { | |
event.start({ | |
type: "start", | |
alpha: alpha = x | |
}); | |
d3.timer(force.tick); | |
} | |
return force; | |
}; | |
force.start = function() { | |
var i, n = nodes.length, m = links.length, w = size[0], h = size[1], neighbors, o; | |
for (i = 0; i < n; ++i) { | |
(o = nodes[i]).index = i; | |
o.weight = 0; | |
} | |
for (i = 0; i < m; ++i) { | |
o = links[i]; | |
if (typeof o.source == "number") o.source = nodes[o.source]; | |
if (typeof o.target == "number") o.target = nodes[o.target]; | |
++o.source.weight; | |
++o.target.weight; | |
} | |
for (i = 0; i < n; ++i) { | |
o = nodes[i]; | |
if (isNaN(o.x)) o.x = position("x", w); | |
if (isNaN(o.y)) o.y = position("y", h); | |
if (isNaN(o.px)) o.px = o.x; | |
if (isNaN(o.py)) o.py = o.y; | |
} | |
distances = []; | |
if (typeof linkDistance === "function") for (i = 0; i < m; ++i) distances[i] = +linkDistance.call(this, links[i], i); else for (i = 0; i < m; ++i) distances[i] = linkDistance; | |
strengths = []; | |
if (typeof linkStrength === "function") for (i = 0; i < m; ++i) strengths[i] = +linkStrength.call(this, links[i], i); else for (i = 0; i < m; ++i) strengths[i] = linkStrength; | |
charges = []; | |
if (typeof charge === "function") for (i = 0; i < n; ++i) charges[i] = +charge.call(this, nodes[i], i); else for (i = 0; i < n; ++i) charges[i] = charge; | |
function position(dimension, size) { | |
if (!neighbors) { | |
neighbors = new Array(n); | |
for (j = 0; j < n; ++j) { | |
neighbors[j] = []; | |
} | |
for (j = 0; j < m; ++j) { | |
var o = links[j]; | |
neighbors[o.source.index].push(o.target); | |
neighbors[o.target.index].push(o.source); | |
} | |
} | |
var candidates = neighbors[i], j = -1, l = candidates.length, x; | |
while (++j < l) if (!isNaN(x = candidates[j][dimension])) return x; | |
return Math.random() * size; | |
} | |
return force.resume(); | |
}; | |
force.resume = function() { | |
return force.alpha(.1); | |
}; | |
force.stop = function() { | |
return force.alpha(0); | |
}; | |
force.drag = function() { | |
if (!drag) drag = d3.behavior.drag().origin(d3_identity).on("dragstart.force", d3_layout_forceDragstart).on("drag.force", dragmove).on("dragend.force", d3_layout_forceDragend); | |
if (!arguments.length) return drag; | |
this.on("mouseover.force", d3_layout_forceMouseover).on("mouseout.force", d3_layout_forceMouseout).call(drag); | |
}; | |
function dragmove(d) { | |
d.px = d3.event.x, d.py = d3.event.y; | |
force.resume(); | |
} | |
return d3.rebind(force, event, "on"); | |
}; | |
function d3_layout_forceDragstart(d) { | |
d.fixed |= 2; | |
} | |
function d3_layout_forceDragend(d) { | |
d.fixed &= ~6; | |
} | |
function d3_layout_forceMouseover(d) { | |
d.fixed |= 4; | |
d.px = d.x, d.py = d.y; | |
} | |
function d3_layout_forceMouseout(d) { | |
d.fixed &= ~4; | |
} | |
function d3_layout_forceAccumulate(quad, alpha, charges) { | |
var cx = 0, cy = 0; | |
quad.charge = 0; | |
if (!quad.leaf) { | |
var nodes = quad.nodes, n = nodes.length, i = -1, c; | |
while (++i < n) { | |
c = nodes[i]; | |
if (c == null) continue; | |
d3_layout_forceAccumulate(c, alpha, charges); | |
quad.charge += c.charge; | |
cx += c.charge * c.cx; | |
cy += c.charge * c.cy; | |
} | |
} | |
if (quad.point) { | |
if (!quad.leaf) { | |
quad.point.x += Math.random() - .5; | |
quad.point.y += Math.random() - .5; | |
} | |
var k = alpha * charges[quad.point.index]; | |
quad.charge += quad.pointCharge = k; | |
cx += k * quad.point.x; | |
cy += k * quad.point.y; | |
} | |
quad.cx = cx / quad.charge; | |
quad.cy = cy / quad.charge; | |
} | |
var d3_layout_forceLinkDistance = 20, d3_layout_forceLinkStrength = 1, d3_layout_forceChargeDistance2 = Infinity; | |
d3.layout.hierarchy = function() { | |
var sort = d3_layout_hierarchySort, children = d3_layout_hierarchyChildren, value = d3_layout_hierarchyValue; | |
function hierarchy(root) { | |
var stack = [ root ], nodes = [], node; | |
root.depth = 0; | |
while ((node = stack.pop()) != null) { | |
nodes.push(node); | |
if ((childs = children.call(hierarchy, node, node.depth)) && (n = childs.length)) { | |
var n, childs, child; | |
while (--n >= 0) { | |
stack.push(child = childs[n]); | |
child.parent = node; | |
child.depth = node.depth + 1; | |
} | |
if (value) node.value = 0; | |
node.children = childs; | |
} else { | |
if (value) node.value = +value.call(hierarchy, node, node.depth) || 0; | |
delete node.children; | |
} | |
} | |
d3_layout_hierarchyVisitAfter(root, function(node) { | |
var childs, parent; | |
if (sort && (childs = node.children)) childs.sort(sort); | |
if (value && (parent = node.parent)) parent.value += node.value; | |
}); | |
return nodes; | |
} | |
hierarchy.sort = function(x) { | |
if (!arguments.length) return sort; | |
sort = x; | |
return hierarchy; | |
}; | |
hierarchy.children = function(x) { | |
if (!arguments.length) return children; | |
children = x; | |
return hierarchy; | |
}; | |
hierarchy.value = function(x) { | |
if (!arguments.length) return value; | |
value = x; | |
return hierarchy; | |
}; | |
hierarchy.revalue = function(root) { | |
if (value) { | |
d3_layout_hierarchyVisitBefore(root, function(node) { | |
if (node.children) node.value = 0; | |
}); | |
d3_layout_hierarchyVisitAfter(root, function(node) { | |
var parent; | |
if (!node.children) node.value = +value.call(hierarchy, node, node.depth) || 0; | |
if (parent = node.parent) parent.value += node.value; | |
}); | |
} | |
return root; | |
}; | |
return hierarchy; | |
}; | |
function d3_layout_hierarchyRebind(object, hierarchy) { | |
d3.rebind(object, hierarchy, "sort", "children", "value"); | |
object.nodes = object; | |
object.links = d3_layout_hierarchyLinks; | |
return object; | |
} | |
function d3_layout_hierarchyVisitBefore(node, callback) { | |
var nodes = [ node ]; | |
while ((node = nodes.pop()) != null) { | |
callback(node); | |
if ((children = node.children) && (n = children.length)) { | |
var n, children; | |
while (--n >= 0) nodes.push(children[n]); | |
} | |
} | |
} | |
function d3_layout_hierarchyVisitAfter(node, callback) { | |
var nodes = [ node ], nodes2 = []; | |
while ((node = nodes.pop()) != null) { | |
nodes2.push(node); | |
if ((children = node.children) && (n = children.length)) { | |
var i = -1, n, children; | |
while (++i < n) nodes.push(children[i]); | |
} | |
} | |
while ((node = nodes2.pop()) != null) { | |
callback(node); | |
} | |
} | |
function d3_layout_hierarchyChildren(d) { | |
return d.children; | |
} | |
function d3_layout_hierarchyValue(d) { | |
return d.value; | |
} | |
function d3_layout_hierarchySort(a, b) { | |
return b.value - a.value; | |
} | |
function d3_layout_hierarchyLinks(nodes) { | |
return d3.merge(nodes.map(function(parent) { | |
return (parent.children || []).map(function(child) { | |
return { | |
source: parent, | |
target: child | |
}; | |
}); | |
})); | |
} | |
d3.layout.partition = function() { | |
var hierarchy = d3.layout.hierarchy(), size = [ 1, 1 ]; | |
function position(node, x, dx, dy) { | |
var children = node.children; | |
node.x = x; | |
node.y = node.depth * dy; | |
node.dx = dx; | |
node.dy = dy; | |
if (children && (n = children.length)) { | |
var i = -1, n, c, d; | |
dx = node.value ? dx / node.value : 0; | |
while (++i < n) { | |
position(c = children[i], x, d = c.value * dx, dy); | |
x += d; | |
} | |
} | |
} | |
function depth(node) { | |
var children = node.children, d = 0; | |
if (children && (n = children.length)) { | |
var i = -1, n; | |
while (++i < n) d = Math.max(d, depth(children[i])); | |
} | |
return 1 + d; | |
} | |
function partition(d, i) { | |
var nodes = hierarchy.call(this, d, i); | |
position(nodes[0], 0, size[0], size[1] / depth(nodes[0])); | |
return nodes; | |
} | |
partition.size = function(x) { | |
if (!arguments.length) return size; | |
size = x; | |
return partition; | |
}; | |
return d3_layout_hierarchyRebind(partition, hierarchy); | |
}; | |
d3.layout.pie = function() { | |
var value = Number, sort = d3_layout_pieSortByValue, startAngle = 0, endAngle = τ, padAngle = 0; | |
function pie(data) { | |
var n = data.length, values = data.map(function(d, i) { | |
return +value.call(pie, d, i); | |
}), a = +(typeof startAngle === "function" ? startAngle.apply(this, arguments) : startAngle), da = (typeof endAngle === "function" ? endAngle.apply(this, arguments) : endAngle) - a, p = Math.min(Math.abs(da) / n, +(typeof padAngle === "function" ? padAngle.apply(this, arguments) : padAngle)), pa = p * (da < 0 ? -1 : 1), k = (da - n * pa) / d3.sum(values), index = d3.range(n), arcs = [], v; | |
if (sort != null) index.sort(sort === d3_layout_pieSortByValue ? function(i, j) { | |
return values[j] - values[i]; | |
} : function(i, j) { | |
return sort(data[i], data[j]); | |
}); | |
index.forEach(function(i) { | |
arcs[i] = { | |
data: data[i], | |
value: v = values[i], | |
startAngle: a, | |
endAngle: a += v * k + pa, | |
padAngle: p | |
}; | |
}); | |
return arcs; | |
} | |
pie.value = function(_) { | |
if (!arguments.length) return value; | |
value = _; | |
return pie; | |
}; | |
pie.sort = function(_) { | |
if (!arguments.length) return sort; | |
sort = _; | |
return pie; | |
}; | |
pie.startAngle = function(_) { | |
if (!arguments.length) return startAngle; | |
startAngle = _; | |
return pie; | |
}; | |
pie.endAngle = function(_) { | |
if (!arguments.length) return endAngle; | |
endAngle = _; | |
return pie; | |
}; | |
pie.padAngle = function(_) { | |
if (!arguments.length) return padAngle; | |
padAngle = _; | |
return pie; | |
}; | |
return pie; | |
}; | |
var d3_layout_pieSortByValue = {}; | |
d3.layout.stack = function() { | |
var values = d3_identity, order = d3_layout_stackOrderDefault, offset = d3_layout_stackOffsetZero, out = d3_layout_stackOut, x = d3_layout_stackX, y = d3_layout_stackY; | |
function stack(data, index) { | |
if (!(n = data.length)) return data; | |
var series = data.map(function(d, i) { | |
return values.call(stack, d, i); | |
}); | |
var points = series.map(function(d) { | |
return d.map(function(v, i) { | |
return [ x.call(stack, v, i), y.call(stack, v, i) ]; | |
}); | |
}); | |
var orders = order.call(stack, points, index); | |
series = d3.permute(series, orders); | |
points = d3.permute(points, orders); | |
var offsets = offset.call(stack, points, index); | |
var m = series[0].length, n, i, j, o; | |
for (j = 0; j < m; ++j) { | |
out.call(stack, series[0][j], o = offsets[j], points[0][j][1]); | |
for (i = 1; i < n; ++i) { | |
out.call(stack, series[i][j], o += points[i - 1][j][1], points[i][j][1]); | |
} | |
} | |
return data; | |
} | |
stack.values = function(x) { | |
if (!arguments.length) return values; | |
values = x; | |
return stack; | |
}; | |
stack.order = function(x) { | |
if (!arguments.length) return order; | |
order = typeof x === "function" ? x : d3_layout_stackOrders.get(x) || d3_layout_stackOrderDefault; | |
return stack; | |
}; | |
stack.offset = function(x) { | |
if (!arguments.length) return offset; | |
offset = typeof x === "function" ? x : d3_layout_stackOffsets.get(x) || d3_layout_stackOffsetZero; | |
return stack; | |
}; | |
stack.x = function(z) { | |
if (!arguments.length) return x; | |
x = z; | |
return stack; | |
}; | |
stack.y = function(z) { | |
if (!arguments.length) return y; | |
y = z; | |
return stack; | |
}; | |
stack.out = function(z) { | |
if (!arguments.length) return out; | |
out = z; | |
return stack; | |
}; | |
return stack; | |
}; | |
function d3_layout_stackX(d) { | |
return d.x; | |
} | |
function d3_layout_stackY(d) { | |
return d.y; | |
} | |
function d3_layout_stackOut(d, y0, y) { | |
d.y0 = y0; | |
d.y = y; | |
} | |
var d3_layout_stackOrders = d3.map({ | |
"inside-out": function(data) { | |
var n = data.length, i, j, max = data.map(d3_layout_stackMaxIndex), sums = data.map(d3_layout_stackReduceSum), index = d3.range(n).sort(function(a, b) { | |
return max[a] - max[b]; | |
}), top = 0, bottom = 0, tops = [], bottoms = []; | |
for (i = 0; i < n; ++i) { | |
j = index[i]; | |
if (top < bottom) { | |
top += sums[j]; | |
tops.push(j); | |
} else { | |
bottom += sums[j]; | |
bottoms.push(j); | |
} | |
} | |
return bottoms.reverse().concat(tops); | |
}, | |
reverse: function(data) { | |
return d3.range(data.length).reverse(); | |
}, | |
"default": d3_layout_stackOrderDefault | |
}); | |
var d3_layout_stackOffsets = d3.map({ | |
silhouette: function(data) { | |
var n = data.length, m = data[0].length, sums = [], max = 0, i, j, o, y0 = []; | |
for (j = 0; j < m; ++j) { | |
for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; | |
if (o > max) max = o; | |
sums.push(o); | |
} | |
for (j = 0; j < m; ++j) { | |
y0[j] = (max - sums[j]) / 2; | |
} | |
return y0; | |
}, | |
wiggle: function(data) { | |
var n = data.length, x = data[0], m = x.length, i, j, k, s1, s2, s3, dx, o, o0, y0 = []; | |
y0[0] = o = o0 = 0; | |
for (j = 1; j < m; ++j) { | |
for (i = 0, s1 = 0; i < n; ++i) s1 += data[i][j][1]; | |
for (i = 0, s2 = 0, dx = x[j][0] - x[j - 1][0]; i < n; ++i) { | |
for (k = 0, s3 = (data[i][j][1] - data[i][j - 1][1]) / (2 * dx); k < i; ++k) { | |
s3 += (data[k][j][1] - data[k][j - 1][1]) / dx; | |
} | |
s2 += s3 * data[i][j][1]; | |
} | |
y0[j] = o -= s1 ? s2 / s1 * dx : 0; | |
if (o < o0) o0 = o; | |
} | |
for (j = 0; j < m; ++j) y0[j] -= o0; | |
return y0; | |
}, | |
expand: function(data) { | |
var n = data.length, m = data[0].length, k = 1 / n, i, j, o, y0 = []; | |
for (j = 0; j < m; ++j) { | |
for (i = 0, o = 0; i < n; i++) o += data[i][j][1]; | |
if (o) for (i = 0; i < n; i++) data[i][j][1] /= o; else for (i = 0; i < n; i++) data[i][j][1] = k; | |
} | |
for (j = 0; j < m; ++j) y0[j] = 0; | |
return y0; | |
}, | |
zero: d3_layout_stackOffsetZero | |
}); | |
function d3_layout_stackOrderDefault(data) { | |
return d3.range(data.length); | |
} | |
function d3_layout_stackOffsetZero(data) { | |
var j = -1, m = data[0].length, y0 = []; | |
while (++j < m) y0[j] = 0; | |
return y0; | |
} | |
function d3_layout_stackMaxIndex(array) { | |
var i = 1, j = 0, v = array[0][1], k, n = array.length; | |
for (;i < n; ++i) { | |
if ((k = array[i][1]) > v) { | |
j = i; | |
v = k; | |
} | |
} | |
return j; | |
} | |
function d3_layout_stackReduceSum(d) { | |
return d.reduce(d3_layout_stackSum, 0); | |
} | |
function d3_layout_stackSum(p, d) { | |
return p + d[1]; | |
} | |
d3.layout.histogram = function() { | |
var frequency = true, valuer = Number, ranger = d3_layout_histogramRange, binner = d3_layout_histogramBinSturges; | |
function histogram(data, i) { | |
var bins = [], values = data.map(valuer, this), range = ranger.call(this, values, i), thresholds = binner.call(this, range, values, i), bin, i = -1, n = values.length, m = thresholds.length - 1, k = frequency ? 1 : 1 / n, x; | |
while (++i < m) { | |
bin = bins[i] = []; | |
bin.dx = thresholds[i + 1] - (bin.x = thresholds[i]); | |
bin.y = 0; | |
} | |
if (m > 0) { | |
i = -1; | |
while (++i < n) { | |
x = values[i]; | |
if (x >= range[0] && x <= range[1]) { | |
bin = bins[d3.bisect(thresholds, x, 1, m) - 1]; | |
bin.y += k; | |
bin.push(data[i]); | |
} | |
} | |
} | |
return bins; | |
} | |
histogram.value = function(x) { | |
if (!arguments.length) return valuer; | |
valuer = x; | |
return histogram; | |
}; | |
histogram.range = function(x) { | |
if (!arguments.length) return ranger; | |
ranger = d3_functor(x); | |
return histogram; | |
}; | |
histogram.bins = function(x) { | |
if (!arguments.length) return binner; | |
binner = typeof x === "number" ? function(range) { | |
return d3_layout_histogramBinFixed(range, x); | |
} : d3_functor(x); | |
return histogram; | |
}; | |
histogram.frequency = function(x) { | |
if (!arguments.length) return frequency; | |
frequency = !!x; | |
return histogram; | |
}; | |
return histogram; | |
}; | |
function d3_layout_histogramBinSturges(range, values) { | |
return d3_layout_histogramBinFixed(range, Math.ceil(Math.log(values.length) / Math.LN2 + 1)); | |
} | |
function d3_layout_histogramBinFixed(range, n) { | |
var x = -1, b = +range[0], m = (range[1] - b) / n, f = []; | |
while (++x <= n) f[x] = m * x + b; | |
return f; | |
} | |
function d3_layout_histogramRange(values) { | |
return [ d3.min(values), d3.max(values) ]; | |
} | |
d3.layout.pack = function() { | |
var hierarchy = d3.layout.hierarchy().sort(d3_layout_packSort), padding = 0, size = [ 1, 1 ], radius; | |
function pack(d, i) { | |
var nodes = hierarchy.call(this, d, i), root = nodes[0], w = size[0], h = size[1], r = radius == null ? Math.sqrt : typeof radius === "function" ? radius : function() { | |
return radius; | |
}; | |
root.x = root.y = 0; | |
d3_layout_hierarchyVisitAfter(root, function(d) { | |
d.r = +r(d.value); | |
}); | |
d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); | |
if (padding) { | |
var dr = padding * (radius ? 1 : Math.max(2 * root.r / w, 2 * root.r / h)) / 2; | |
d3_layout_hierarchyVisitAfter(root, function(d) { | |
d.r += dr; | |
}); | |
d3_layout_hierarchyVisitAfter(root, d3_layout_packSiblings); | |
d3_layout_hierarchyVisitAfter(root, function(d) { | |
d.r -= dr; | |
}); | |
} | |
d3_layout_packTransform(root, w / 2, h / 2, radius ? 1 : 1 / Math.max(2 * root.r / w, 2 * root.r / h)); | |
return nodes; | |
} | |
pack.size = function(_) { | |
if (!arguments.length) return size; | |
size = _; | |
return pack; | |
}; | |
pack.radius = function(_) { | |
if (!arguments.length) return radius; | |
radius = _ == null || typeof _ === "function" ? _ : +_; | |
return pack; | |
}; | |
pack.padding = function(_) { | |
if (!arguments.length) return padding; | |
padding = +_; | |
return pack; | |
}; | |
return d3_layout_hierarchyRebind(pack, hierarchy); | |
}; | |
function d3_layout_packSort(a, b) { | |
return a.value - b.value; | |
} | |
function d3_layout_packInsert(a, b) { | |
var c = a._pack_next; | |
a._pack_next = b; | |
b._pack_prev = a; | |
b._pack_next = c; | |
c._pack_prev = b; | |
} | |
function d3_layout_packSplice(a, b) { | |
a._pack_next = b; | |
b._pack_prev = a; | |
} | |
function d3_layout_packIntersects(a, b) { | |
var dx = b.x - a.x, dy = b.y - a.y, dr = a.r + b.r; | |
return .999 * dr * dr > dx * dx + dy * dy; | |
} | |
function d3_layout_packSiblings(node) { | |
if (!(nodes = node.children) || !(n = nodes.length)) return; | |
var nodes, xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, a, b, c, i, j, k, n; | |
function bound(node) { | |
xMin = Math.min(node.x - node.r, xMin); | |
xMax = Math.max(node.x + node.r, xMax); | |
yMin = Math.min(node.y - node.r, yMin); | |
yMax = Math.max(node.y + node.r, yMax); | |
} | |
nodes.forEach(d3_layout_packLink); | |
a = nodes[0]; | |
a.x = -a.r; | |
a.y = 0; | |
bound(a); | |
if (n > 1) { | |
b = nodes[1]; | |
b.x = b.r; | |
b.y = 0; | |
bound(b); | |
if (n > 2) { | |
c = nodes[2]; | |
d3_layout_packPlace(a, b, c); | |
bound(c); | |
d3_layout_packInsert(a, c); | |
a._pack_prev = c; | |
d3_layout_packInsert(c, b); | |
b = a._pack_next; | |
for (i = 3; i < n; i++) { | |
d3_layout_packPlace(a, b, c = nodes[i]); | |
var isect = 0, s1 = 1, s2 = 1; | |
for (j = b._pack_next; j !== b; j = j._pack_next, s1++) { | |
if (d3_layout_packIntersects(j, c)) { | |
isect = 1; | |
break; | |
} | |
} | |
if (isect == 1) { | |
for (k = a._pack_prev; k !== j._pack_prev; k = k._pack_prev, s2++) { | |
if (d3_layout_packIntersects(k, c)) { | |
break; | |
} | |
} | |
} | |
if (isect) { | |
if (s1 < s2 || s1 == s2 && b.r < a.r) d3_layout_packSplice(a, b = j); else d3_layout_packSplice(a = k, b); | |
i--; | |
} else { | |
d3_layout_packInsert(a, c); | |
b = c; | |
bound(c); | |
} | |
} | |
} | |
} | |
var cx = (xMin + xMax) / 2, cy = (yMin + yMax) / 2, cr = 0; | |
for (i = 0; i < n; i++) { | |
c = nodes[i]; | |
c.x -= cx; | |
c.y -= cy; | |
cr = Math.max(cr, c.r + Math.sqrt(c.x * c.x + c.y * c.y)); | |
} | |
node.r = cr; | |
nodes.forEach(d3_layout_packUnlink); | |
} | |
function d3_layout_packLink(node) { | |
node._pack_next = node._pack_prev = node; | |
} | |
function d3_layout_packUnlink(node) { | |
delete node._pack_next; | |
delete node._pack_prev; | |
} | |
function d3_layout_packTransform(node, x, y, k) { | |
var children = node.children; | |
node.x = x += k * node.x; | |
node.y = y += k * node.y; | |
node.r *= k; | |
if (children) { | |
var i = -1, n = children.length; | |
while (++i < n) d3_layout_packTransform(children[i], x, y, k); | |
} | |
} | |
function d3_layout_packPlace(a, b, c) { | |
var db = a.r + c.r, dx = b.x - a.x, dy = b.y - a.y; | |
if (db && (dx || dy)) { | |
var da = b.r + c.r, dc = dx * dx + dy * dy; | |
da *= da; | |
db *= db; | |
var x = .5 + (db - da) / (2 * dc), y = Math.sqrt(Math.max(0, 2 * da * (db + dc) - (db -= dc) * db - da * da)) / (2 * dc); | |
c.x = a.x + x * dx + y * dy; | |
c.y = a.y + x * dy - y * dx; | |
} else { | |
c.x = a.x + db; | |
c.y = a.y; | |
} | |
} | |
d3.layout.tree = function() { | |
var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = null; | |
function tree(d, i) { | |
var nodes = hierarchy.call(this, d, i), root0 = nodes[0], root1 = wrapTree(root0); | |
d3_layout_hierarchyVisitAfter(root1, firstWalk), root1.parent.m = -root1.z; | |
d3_layout_hierarchyVisitBefore(root1, secondWalk); | |
if (nodeSize) d3_layout_hierarchyVisitBefore(root0, sizeNode); else { | |
var left = root0, right = root0, bottom = root0; | |
d3_layout_hierarchyVisitBefore(root0, function(node) { | |
if (node.x < left.x) left = node; | |
if (node.x > right.x) right = node; | |
if (node.depth > bottom.depth) bottom = node; | |
}); | |
var tx = separation(left, right) / 2 - left.x, kx = size[0] / (right.x + separation(right, left) / 2 + tx), ky = size[1] / (bottom.depth || 1); | |
d3_layout_hierarchyVisitBefore(root0, function(node) { | |
node.x = (node.x + tx) * kx; | |
node.y = node.depth * ky; | |
}); | |
} | |
return nodes; | |
} | |
function wrapTree(root0) { | |
var root1 = { | |
A: null, | |
children: [ root0 ] | |
}, queue = [ root1 ], node1; | |
while ((node1 = queue.pop()) != null) { | |
for (var children = node1.children, child, i = 0, n = children.length; i < n; ++i) { | |
queue.push((children[i] = child = { | |
_: children[i], | |
parent: node1, | |
children: (child = children[i].children) && child.slice() || [], | |
A: null, | |
a: null, | |
z: 0, | |
m: 0, | |
c: 0, | |
s: 0, | |
t: null, | |
i: i | |
}).a = child); | |
} | |
} | |
return root1.children[0]; | |
} | |
function firstWalk(v) { | |
var children = v.children, siblings = v.parent.children, w = v.i ? siblings[v.i - 1] : null; | |
if (children.length) { | |
d3_layout_treeShift(v); | |
var midpoint = (children[0].z + children[children.length - 1].z) / 2; | |
if (w) { | |
v.z = w.z + separation(v._, w._); | |
v.m = v.z - midpoint; | |
} else { | |
v.z = midpoint; | |
} | |
} else if (w) { | |
v.z = w.z + separation(v._, w._); | |
} | |
v.parent.A = apportion(v, w, v.parent.A || siblings[0]); | |
} | |
function secondWalk(v) { | |
v._.x = v.z + v.parent.m; | |
v.m += v.parent.m; | |
} | |
function apportion(v, w, ancestor) { | |
if (w) { | |
var vip = v, vop = v, vim = w, vom = vip.parent.children[0], sip = vip.m, sop = vop.m, sim = vim.m, som = vom.m, shift; | |
while (vim = d3_layout_treeRight(vim), vip = d3_layout_treeLeft(vip), vim && vip) { | |
vom = d3_layout_treeLeft(vom); | |
vop = d3_layout_treeRight(vop); | |
vop.a = v; | |
shift = vim.z + sim - vip.z - sip + separation(vim._, vip._); | |
if (shift > 0) { | |
d3_layout_treeMove(d3_layout_treeAncestor(vim, v, ancestor), v, shift); | |
sip += shift; | |
sop += shift; | |
} | |
sim += vim.m; | |
sip += vip.m; | |
som += vom.m; | |
sop += vop.m; | |
} | |
if (vim && !d3_layout_treeRight(vop)) { | |
vop.t = vim; | |
vop.m += sim - sop; | |
} | |
if (vip && !d3_layout_treeLeft(vom)) { | |
vom.t = vip; | |
vom.m += sip - som; | |
ancestor = v; | |
} | |
} | |
return ancestor; | |
} | |
function sizeNode(node) { | |
node.x *= size[0]; | |
node.y = node.depth * size[1]; | |
} | |
tree.separation = function(x) { | |
if (!arguments.length) return separation; | |
separation = x; | |
return tree; | |
}; | |
tree.size = function(x) { | |
if (!arguments.length) return nodeSize ? null : size; | |
nodeSize = (size = x) == null ? sizeNode : null; | |
return tree; | |
}; | |
tree.nodeSize = function(x) { | |
if (!arguments.length) return nodeSize ? size : null; | |
nodeSize = (size = x) == null ? null : sizeNode; | |
return tree; | |
}; | |
return d3_layout_hierarchyRebind(tree, hierarchy); | |
}; | |
function d3_layout_treeSeparation(a, b) { | |
return a.parent == b.parent ? 1 : 2; | |
} | |
function d3_layout_treeLeft(v) { | |
var children = v.children; | |
return children.length ? children[0] : v.t; | |
} | |
function d3_layout_treeRight(v) { | |
var children = v.children, n; | |
return (n = children.length) ? children[n - 1] : v.t; | |
} | |
function d3_layout_treeMove(wm, wp, shift) { | |
var change = shift / (wp.i - wm.i); | |
wp.c -= change; | |
wp.s += shift; | |
wm.c += change; | |
wp.z += shift; | |
wp.m += shift; | |
} | |
function d3_layout_treeShift(v) { | |
var shift = 0, change = 0, children = v.children, i = children.length, w; | |
while (--i >= 0) { | |
w = children[i]; | |
w.z += shift; | |
w.m += shift; | |
shift += w.s + (change += w.c); | |
} | |
} | |
function d3_layout_treeAncestor(vim, v, ancestor) { | |
return vim.a.parent === v.parent ? vim.a : ancestor; | |
} | |
d3.layout.cluster = function() { | |
var hierarchy = d3.layout.hierarchy().sort(null).value(null), separation = d3_layout_treeSeparation, size = [ 1, 1 ], nodeSize = false; | |
function cluster(d, i) { | |
var nodes = hierarchy.call(this, d, i), root = nodes[0], previousNode, x = 0; | |
d3_layout_hierarchyVisitAfter(root, function(node) { | |
var children = node.children; | |
if (children && children.length) { | |
node.x = d3_layout_clusterX(children); | |
node.y = d3_layout_clusterY(children); | |
} else { | |
node.x = previousNode ? x += separation(node, previousNode) : 0; | |
node.y = 0; | |
previousNode = node; | |
} | |
}); | |
var left = d3_layout_clusterLeft(root), right = d3_layout_clusterRight(root), x0 = left.x - separation(left, right) / 2, x1 = right.x + separation(right, left) / 2; | |
d3_layout_hierarchyVisitAfter(root, nodeSize ? function(node) { | |
node.x = (node.x - root.x) * size[0]; | |
node.y = (root.y - node.y) * size[1]; | |
} : function(node) { | |
node.x = (node.x - x0) / (x1 - x0) * size[0]; | |
node.y = (1 - (root.y ? node.y / root.y : 1)) * size[1]; | |
}); | |
return nodes; | |
} | |
cluster.separation = function(x) { | |
if (!arguments.length) return separation; | |
separation = x; | |
return cluster; | |
}; | |
cluster.size = function(x) { | |
if (!arguments.length) return nodeSize ? null : size; | |
nodeSize = (size = x) == null; | |
return cluster; | |
}; | |
cluster.nodeSize = function(x) { | |
if (!arguments.length) return nodeSize ? size : null; | |
nodeSize = (size = x) != null; | |
return cluster; | |
}; | |
return d3_layout_hierarchyRebind(cluster, hierarchy); | |
}; | |
function d3_layout_clusterY(children) { | |
return 1 + d3.max(children, function(child) { | |
return child.y; | |
}); | |
} | |
function d3_layout_clusterX(children) { | |
return children.reduce(function(x, child) { | |
return x + child.x; | |
}, 0) / children.length; | |
} | |
function d3_layout_clusterLeft(node) { | |
var children = node.children; | |
return children && children.length ? d3_layout_clusterLeft(children[0]) : node; | |
} | |
function d3_layout_clusterRight(node) { | |
var children = node.children, n; | |
return children && (n = children.length) ? d3_layout_clusterRight(children[n - 1]) : node; | |
} | |
d3.layout.treemap = function() { | |
var hierarchy = d3.layout.hierarchy(), round = Math.round, size = [ 1, 1 ], padding = null, pad = d3_layout_treemapPadNull, sticky = false, stickies, mode = "squarify", ratio = .5 * (1 + Math.sqrt(5)); | |
function scale(children, k) { | |
var i = -1, n = children.length, child, area; | |
while (++i < n) { | |
area = (child = children[i]).value * (k < 0 ? 0 : k); | |
child.area = isNaN(area) || area <= 0 ? 0 : area; | |
} | |
} | |
function squarify(node) { | |
var children = node.children; | |
if (children && children.length) { | |
var rect = pad(node), row = [], remaining = children.slice(), child, best = Infinity, score, u = mode === "slice" ? rect.dx : mode === "dice" ? rect.dy : mode === "slice-dice" ? node.depth & 1 ? rect.dy : rect.dx : Math.min(rect.dx, rect.dy), n; | |
scale(remaining, rect.dx * rect.dy / node.value); | |
row.area = 0; | |
while ((n = remaining.length) > 0) { | |
row.push(child = remaining[n - 1]); | |
row.area += child.area; | |
if (mode !== "squarify" || (score = worst(row, u)) <= best) { | |
remaining.pop(); | |
best = score; | |
} else { | |
row.area -= row.pop().area; | |
position(row, u, rect, false); | |
u = Math.min(rect.dx, rect.dy); | |
row.length = row.area = 0; | |
best = Infinity; | |
} | |
} | |
if (row.length) { | |
position(row, u, rect, true); | |
row.length = row.area = 0; | |
} | |
children.forEach(squarify); | |
} | |
} | |
function stickify(node) { | |
var children = node.children; | |
if (children && children.length) { | |
var rect = pad(node), remaining = children.slice(), child, row = []; | |
scale(remaining, rect.dx * rect.dy / node.value); | |
row.area = 0; | |
while (child = remaining.pop()) { | |
row.push(child); | |
row.area += child.area; | |
if (child.z != null) { | |
position(row, child.z ? rect.dx : rect.dy, rect, !remaining.length); | |
row.length = row.area = 0; | |
} | |
} | |
children.forEach(stickify); | |
} | |
} | |
function worst(row, u) { | |
var s = row.area, r, rmax = 0, rmin = Infinity, i = -1, n = row.length; | |
while (++i < n) { | |
if (!(r = row[i].area)) continue; | |
if (r < rmin) rmin = r; | |
if (r > rmax) rmax = r; | |
} | |
s *= s; | |
u *= u; | |
return s ? Math.max(u * rmax * ratio / s, s / (u * rmin * ratio)) : Infinity; | |
} | |
function position(row, u, rect, flush) { | |
var i = -1, n = row.length, x = rect.x, y = rect.y, v = u ? round(row.area / u) : 0, o; | |
if (u == rect.dx) { | |
if (flush || v > rect.dy) v = rect.dy; | |
while (++i < n) { | |
o = row[i]; | |
o.x = x; | |
o.y = y; | |
o.dy = v; | |
x += o.dx = Math.min(rect.x + rect.dx - x, v ? round(o.area / v) : 0); | |
} | |
o.z = true; | |
o.dx += rect.x + rect.dx - x; | |
rect.y += v; | |
rect.dy -= v; | |
} else { | |
if (flush || v > rect.dx) v = rect.dx; | |
while (++i < n) { | |
o = row[i]; | |
o.x = x; | |
o.y = y; | |
o.dx = v; | |
y += o.dy = Math.min(rect.y + rect.dy - y, v ? round(o.area / v) : 0); | |
} | |
o.z = false; | |
o.dy += rect.y + rect.dy - y; | |
rect.x += v; | |
rect.dx -= v; | |
} | |
} | |
function treemap(d) { | |
var nodes = stickies || hierarchy(d), root = nodes[0]; | |
root.x = 0; | |
root.y = 0; | |
root.dx = size[0]; | |
root.dy = size[1]; | |
if (stickies) hierarchy.revalue(root); | |
scale([ root ], root.dx * root.dy / root.value); | |
(stickies ? stickify : squarify)(root); | |
if (sticky) stickies = nodes; | |
return nodes; | |
} | |
treemap.size = function(x) { | |
if (!arguments.length) return size; | |
size = x; | |
return treemap; | |
}; | |
treemap.padding = function(x) { | |
if (!arguments.length) return padding; | |
function padFunction(node) { | |
var p = x.call(treemap, node, node.depth); | |
return p == null ? d3_layout_treemapPadNull(node) : d3_layout_treemapPad(node, typeof p === "number" ? [ p, p, p, p ] : p); | |
} | |
function padConstant(node) { | |
return d3_layout_treemapPad(node, x); | |
} | |
var type; | |
pad = (padding = x) == null ? d3_layout_treemapPadNull : (type = typeof x) === "function" ? padFunction : type === "number" ? (x = [ x, x, x, x ], | |
padConstant) : padConstant; | |
return treemap; | |
}; | |
treemap.round = function(x) { | |
if (!arguments.length) return round != Number; | |
round = x ? Math.round : Number; | |
return treemap; | |
}; | |
treemap.sticky = function(x) { | |
if (!arguments.length) return sticky; | |
sticky = x; | |
stickies = null; | |
return treemap; | |
}; | |
treemap.ratio = function(x) { | |
if (!arguments.length) return ratio; | |
ratio = x; | |
return treemap; | |
}; | |
treemap.mode = function(x) { | |
if (!arguments.length) return mode; | |
mode = x + ""; | |
return treemap; | |
}; | |
return d3_layout_hierarchyRebind(treemap, hierarchy); | |
}; | |
function d3_layout_treemapPadNull(node) { | |
return { | |
x: node.x, | |
y: node.y, | |
dx: node.dx, | |
dy: node.dy | |
}; | |
} | |
function d3_layout_treemapPad(node, padding) { | |
var x = node.x + padding[3], y = node.y + padding[0], dx = node.dx - padding[1] - padding[3], dy = node.dy - padding[0] - padding[2]; | |
if (dx < 0) { | |
x += dx / 2; | |
dx = 0; | |
} | |
if (dy < 0) { | |
y += dy / 2; | |
dy = 0; | |
} | |
return { | |
x: x, | |
y: y, | |
dx: dx, | |
dy: dy | |
}; | |
} | |
d3.random = { | |
normal: function(µ, σ) { | |
var n = arguments.length; | |
if (n < 2) σ = 1; | |
if (n < 1) µ = 0; | |
return function() { | |
var x, y, r; | |
do { | |
x = Math.random() * 2 - 1; | |
y = Math.random() * 2 - 1; | |
r = x * x + y * y; | |
} while (!r || r > 1); | |
return µ + σ * x * Math.sqrt(-2 * Math.log(r) / r); | |
}; | |
}, | |
logNormal: function() { | |
var random = d3.random.normal.apply(d3, arguments); | |
return function() { | |
return Math.exp(random()); | |
}; | |
}, | |
bates: function(m) { | |
var random = d3.random.irwinHall(m); | |
return function() { | |
return random() / m; | |
}; | |
}, | |
irwinHall: function(m) { | |
return function() { | |
for (var s = 0, j = 0; j < m; j++) s += Math.random(); | |
return s; | |
}; | |
} | |
}; | |
d3.scale = {}; | |
function d3_scaleExtent(domain) { | |
var start = domain[0], stop = domain[domain.length - 1]; | |
return start < stop ? [ start, stop ] : [ stop, start ]; | |
} | |
function d3_scaleRange(scale) { | |
return scale.rangeExtent ? scale.rangeExtent() : d3_scaleExtent(scale.range()); | |
} | |
function d3_scale_bilinear(domain, range, uninterpolate, interpolate) { | |
var u = uninterpolate(domain[0], domain[1]), i = interpolate(range[0], range[1]); | |
return function(x) { | |
return i(u(x)); | |
}; | |
} | |
function d3_scale_nice(domain, nice) { | |
var i0 = 0, i1 = domain.length - 1, x0 = domain[i0], x1 = domain[i1], dx; | |
if (x1 < x0) { | |
dx = i0, i0 = i1, i1 = dx; | |
dx = x0, x0 = x1, x1 = dx; | |
} | |
domain[i0] = nice.floor(x0); | |
domain[i1] = nice.ceil(x1); | |
return domain; | |
} | |
function d3_scale_niceStep(step) { | |
return step ? { | |
floor: function(x) { | |
return Math.floor(x / step) * step; | |
}, | |
ceil: function(x) { | |
return Math.ceil(x / step) * step; | |
} | |
} : d3_scale_niceIdentity; | |
} | |
var d3_scale_niceIdentity = { | |
floor: d3_identity, | |
ceil: d3_identity | |
}; | |
function d3_scale_polylinear(domain, range, uninterpolate, interpolate) { | |
var u = [], i = [], j = 0, k = Math.min(domain.length, range.length) - 1; | |
if (domain[k] < domain[0]) { | |
domain = domain.slice().reverse(); | |
range = range.slice().reverse(); | |
} | |
while (++j <= k) { | |
u.push(uninterpolate(domain[j - 1], domain[j])); | |
i.push(interpolate(range[j - 1], range[j])); | |
} | |
return function(x) { | |
var j = d3.bisect(domain, x, 1, k) - 1; | |
return i[j](u[j](x)); | |
}; | |
} | |
d3.scale.linear = function() { | |
return d3_scale_linear([ 0, 1 ], [ 0, 1 ], d3_interpolate, false); | |
}; | |
function d3_scale_linear(domain, range, interpolate, clamp) { | |
var output, input; | |
function rescale() { | |
var linear = Math.min(domain.length, range.length) > 2 ? d3_scale_polylinear : d3_scale_bilinear, uninterpolate = clamp ? d3_uninterpolateClamp : d3_uninterpolateNumber; | |
output = linear(domain, range, uninterpolate, interpolate); | |
input = linear(range, domain, uninterpolate, d3_interpolate); | |
return scale; | |
} | |
function scale(x) { | |
return output(x); | |
} | |
scale.invert = function(y) { | |
return input(y); | |
}; | |
scale.domain = function(x) { | |
if (!arguments.length) return domain; | |
domain = x.map(Number); | |
return rescale(); | |
}; | |
scale.range = function(x) { | |
if (!arguments.length) return range; | |
range = x; | |
return rescale(); | |
}; | |
scale.rangeRound = function(x) { | |
return scale.range(x).interpolate(d3_interpolateRound); | |
}; | |
scale.clamp = function(x) { | |
if (!arguments.length) return clamp; | |
clamp = x; | |
return rescale(); | |
}; | |
scale.interpolate = function(x) { | |
if (!arguments.length) return interpolate; | |
interpolate = x; | |
return rescale(); | |
}; | |
scale.ticks = function(m) { | |
return d3_scale_linearTicks(domain, m); | |
}; | |
scale.tickFormat = function(m, format) { | |
return d3_scale_linearTickFormat(domain, m, format); | |
}; | |
scale.nice = function(m) { | |
d3_scale_linearNice(domain, m); | |
return rescale(); | |
}; | |
scale.copy = function() { | |
return d3_scale_linear(domain, range, interpolate, clamp); | |
}; | |
return rescale(); | |
} | |
function d3_scale_linearRebind(scale, linear) { | |
return d3.rebind(scale, linear, "range", "rangeRound", "interpolate", "clamp"); | |
} | |
function d3_scale_linearNice(domain, m) { | |
return d3_scale_nice(domain, d3_scale_niceStep(d3_scale_linearTickRange(domain, m)[2])); | |
} | |
function d3_scale_linearTickRange(domain, m) { | |
if (m == null) m = 10; | |
var extent = d3_scaleExtent(domain), span = extent[1] - extent[0], step = Math.pow(10, Math.floor(Math.log(span / m) / Math.LN10)), err = m / span * step; | |
if (err <= .15) step *= 10; else if (err <= .35) step *= 5; else if (err <= .75) step *= 2; | |
extent[0] = Math.ceil(extent[0] / step) * step; | |
extent[1] = Math.floor(extent[1] / step) * step + step * .5; | |
extent[2] = step; | |
return extent; | |
} | |
function d3_scale_linearTicks(domain, m) { | |
return d3.range.apply(d3, d3_scale_linearTickRange(domain, m)); | |
} | |
function d3_scale_linearTickFormat(domain, m, format) { | |
var range = d3_scale_linearTickRange(domain, m); | |
if (format) { | |
var match = d3_format_re.exec(format); | |
match.shift(); | |
if (match[8] === "s") { | |
var prefix = d3.formatPrefix(Math.max(abs(range[0]), abs(range[1]))); | |
if (!match[7]) match[7] = "." + d3_scale_linearPrecision(prefix.scale(range[2])); | |
match[8] = "f"; | |
format = d3.format(match.join("")); | |
return function(d) { | |
return format(prefix.scale(d)) + prefix.symbol; | |
}; | |
} | |
if (!match[7]) match[7] = "." + d3_scale_linearFormatPrecision(match[8], range); | |
format = match.join(""); | |
} else { | |
format = ",." + d3_scale_linearPrecision(range[2]) + "f"; | |
} | |
return d3.format(format); | |
} | |
var d3_scale_linearFormatSignificant = { | |
s: 1, | |
g: 1, | |
p: 1, | |
r: 1, | |
e: 1 | |
}; | |
function d3_scale_linearPrecision(value) { | |
return -Math.floor(Math.log(value) / Math.LN10 + .01); | |
} | |
function d3_scale_linearFormatPrecision(type, range) { | |
var p = d3_scale_linearPrecision(range[2]); | |
return type in d3_scale_linearFormatSignificant ? Math.abs(p - d3_scale_linearPrecision(Math.max(abs(range[0]), abs(range[1])))) + +(type !== "e") : p - (type === "%") * 2; | |
} | |
d3.scale.log = function() { | |
return d3_scale_log(d3.scale.linear().domain([ 0, 1 ]), 10, true, [ 1, 10 ]); | |
}; | |
function d3_scale_log(linear, base, positive, domain) { | |
function log(x) { | |
return (positive ? Math.log(x < 0 ? 0 : x) : -Math.log(x > 0 ? 0 : -x)) / Math.log(base); | |
} | |
function pow(x) { | |
return positive ? Math.pow(base, x) : -Math.pow(base, -x); | |
} | |
function scale(x) { | |
return linear(log(x)); | |
} | |
scale.invert = function(x) { | |
return pow(linear.invert(x)); | |
}; | |
scale.domain = function(x) { | |
if (!arguments.length) return domain; | |
positive = x[0] >= 0; | |
linear.domain((domain = x.map(Number)).map(log)); | |
return scale; | |
}; | |
scale.base = function(_) { | |
if (!arguments.length) return base; | |
base = +_; | |
linear.domain(domain.map(log)); | |
return scale; | |
}; | |
scale.nice = function() { | |
var niced = d3_scale_nice(domain.map(log), positive ? Math : d3_scale_logNiceNegative); | |
linear.domain(niced); | |
domain = niced.map(pow); | |
return scale; | |
}; | |
scale.ticks = function() { | |
var extent = d3_scaleExtent(domain), ticks = [], u = extent[0], v = extent[1], i = Math.floor(log(u)), j = Math.ceil(log(v)), n = base % 1 ? 2 : base; | |
if (isFinite(j - i)) { | |
if (positive) { | |
for (;i < j; i++) for (var k = 1; k < n; k++) ticks.push(pow(i) * k); | |
ticks.push(pow(i)); | |
} else { | |
ticks.push(pow(i)); | |
for (;i++ < j; ) for (var k = n - 1; k > 0; k--) ticks.push(pow(i) * k); | |
} | |
for (i = 0; ticks[i] < u; i++) {} | |
for (j = ticks.length; ticks[j - 1] > v; j--) {} | |
ticks = ticks.slice(i, j); | |
} | |
return ticks; | |
}; | |
scale.tickFormat = function(n, format) { | |
if (!arguments.length) return d3_scale_logFormat; | |
if (arguments.length < 2) format = d3_scale_logFormat; else if (typeof format !== "function") format = d3.format(format); | |
var k = Math.max(.1, n / scale.ticks().length), f = positive ? (e = 1e-12, Math.ceil) : (e = -1e-12, | |
Math.floor), e; | |
return function(d) { | |
return d / pow(f(log(d) + e)) <= k ? format(d) : ""; | |
}; | |
}; | |
scale.copy = function() { | |
return d3_scale_log(linear.copy(), base, positive, domain); | |
}; | |
return d3_scale_linearRebind(scale, linear); | |
} | |
var d3_scale_logFormat = d3.format(".0e"), d3_scale_logNiceNegative = { | |
floor: function(x) { | |
return -Math.ceil(-x); | |
}, | |
ceil: function(x) { | |
return -Math.floor(-x); | |
} | |
}; | |
d3.scale.pow = function() { | |
return d3_scale_pow(d3.scale.linear(), 1, [ 0, 1 ]); | |
}; | |
function d3_scale_pow(linear, exponent, domain) { | |
var powp = d3_scale_powPow(exponent), powb = d3_scale_powPow(1 / exponent); | |
function scale(x) { | |
return linear(powp(x)); | |
} | |
scale.invert = function(x) { | |
return powb(linear.invert(x)); | |
}; | |
scale.domain = function(x) { | |
if (!arguments.length) return domain; | |
linear.domain((domain = x.map(Number)).map(powp)); | |
return scale; | |
}; | |
scale.ticks = function(m) { | |
return d3_scale_linearTicks(domain, m); | |
}; | |
scale.tickFormat = function(m, format) { | |
return d3_scale_linearTickFormat(domain, m, format); | |
}; | |
scale.nice = function(m) { | |
return scale.domain(d3_scale_linearNice(domain, m)); | |
}; | |
scale.exponent = function(x) { | |
if (!arguments.length) return exponent; | |
powp = d3_scale_powPow(exponent = x); | |
powb = d3_scale_powPow(1 / exponent); | |
linear.domain(domain.map(powp)); | |
return scale; | |
}; | |
scale.copy = function() { | |
return d3_scale_pow(linear.copy(), exponent, domain); | |
}; | |
return d3_scale_linearRebind(scale, linear); | |
} | |
function d3_scale_powPow(e) { | |
return function(x) { | |
return x < 0 ? -Math.pow(-x, e) : Math.pow(x, e); | |
}; | |
} | |
d3.scale.sqrt = function() { | |
return d3.scale.pow().exponent(.5); | |
}; | |
d3.scale.ordinal = function() { | |
return d3_scale_ordinal([], { | |
t: "range", | |
a: [ [] ] | |
}); | |
}; | |
function d3_scale_ordinal(domain, ranger) { | |
var index, range, rangeBand; | |
function scale(x) { | |
return range[((index.get(x) || (ranger.t === "range" ? index.set(x, domain.push(x)) : NaN)) - 1) % range.length]; | |
} | |
function steps(start, step) { | |
return d3.range(domain.length).map(function(i) { | |
return start + step * i; | |
}); | |
} | |
scale.domain = function(x) { | |
if (!arguments.length) return domain; | |
domain = []; | |
index = new d3_Map(); | |
var i = -1, n = x.length, xi; | |
while (++i < n) if (!index.has(xi = x[i])) index.set(xi, domain.push(xi)); | |
return scale[ranger.t].apply(scale, ranger.a); | |
}; | |
scale.range = function(x) { | |
if (!arguments.length) return range; | |
range = x; | |
rangeBand = 0; | |
ranger = { | |
t: "range", | |
a: arguments | |
}; | |
return scale; | |
}; | |
scale.rangePoints = function(x, padding) { | |
if (arguments.length < 2) padding = 0; | |
var start = x[0], stop = x[1], step = domain.length < 2 ? (start = (start + stop) / 2, | |
0) : (stop - start) / (domain.length - 1 + padding); | |
range = steps(start + step * padding / 2, step); | |
rangeBand = 0; | |
ranger = { | |
t: "rangePoints", | |
a: arguments | |
}; | |
return scale; | |
}; | |
scale.rangeRoundPoints = function(x, padding) { | |
if (arguments.length < 2) padding = 0; | |
var start = x[0], stop = x[1], step = domain.length < 2 ? (start = stop = Math.round((start + stop) / 2), | |
0) : (stop - start) / (domain.length - 1 + padding) | 0; | |
range = steps(start + Math.round(step * padding / 2 + (stop - start - (domain.length - 1 + padding) * step) / 2), step); | |
rangeBand = 0; | |
ranger = { | |
t: "rangeRoundPoints", | |
a: arguments | |
}; | |
return scale; | |
}; | |
scale.rangeBands = function(x, padding, outerPadding) { | |
if (arguments.length < 2) padding = 0; | |
if (arguments.length < 3) outerPadding = padding; | |
var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = (stop - start) / (domain.length - padding + 2 * outerPadding); | |
range = steps(start + step * outerPadding, step); | |
if (reverse) range.reverse(); | |
rangeBand = step * (1 - padding); | |
ranger = { | |
t: "rangeBands", | |
a: arguments | |
}; | |
return scale; | |
}; | |
scale.rangeRoundBands = function(x, padding, outerPadding) { | |
if (arguments.length < 2) padding = 0; | |
if (arguments.length < 3) outerPadding = padding; | |
var reverse = x[1] < x[0], start = x[reverse - 0], stop = x[1 - reverse], step = Math.floor((stop - start) / (domain.length - padding + 2 * outerPadding)); | |
range = steps(start + Math.round((stop - start - (domain.length - padding) * step) / 2), step); | |
if (reverse) range.reverse(); | |
rangeBand = Math.round(step * (1 - padding)); | |
ranger = { | |
t: "rangeRoundBands", | |
a: arguments | |
}; | |
return scale; | |
}; | |
scale.rangeBand = function() { | |
return rangeBand; | |
}; | |
scale.rangeExtent = function() { | |
return d3_scaleExtent(ranger.a[0]); | |
}; | |
scale.copy = function() { | |
return d3_scale_ordinal(domain, ranger); | |
}; | |
return scale.domain(domain); | |
} | |
d3.scale.category10 = function() { | |
return d3.scale.ordinal().range(d3_category10); | |
}; | |
d3.scale.category20 = function() { | |
return d3.scale.ordinal().range(d3_category20); | |
}; | |
d3.scale.category20b = function() { | |
return d3.scale.ordinal().range(d3_category20b); | |
}; | |
d3.scale.category20c = function() { | |
return d3.scale.ordinal().range(d3_category20c); | |
}; | |
var d3_category10 = [ 2062260, 16744206, 2924588, 14034728, 9725885, 9197131, 14907330, 8355711, 12369186, 1556175 ].map(d3_rgbString); | |
var d3_category20 = [ 2062260, 11454440, 16744206, 16759672, 2924588, 10018698, 14034728, 16750742, 9725885, 12955861, 9197131, 12885140, 14907330, 16234194, 8355711, 13092807, 12369186, 14408589, 1556175, 10410725 ].map(d3_rgbString); | |
var d3_category20b = [ 3750777, 5395619, 7040719, 10264286, 6519097, 9216594, 11915115, 13556636, 9202993, 12426809, 15186514, 15190932, 8666169, 11356490, 14049643, 15177372, 8077683, 10834324, 13528509, 14589654 ].map(d3_rgbString); | |
var d3_category20c = [ 3244733, 7057110, 10406625, 13032431, 15095053, 16616764, 16625259, 16634018, 3253076, 7652470, 10607003, 13101504, 7695281, 10394312, 12369372, 14342891, 6513507, 9868950, 12434877, 14277081 ].map(d3_rgbString); | |
d3.scale.quantile = function() { | |
return d3_scale_quantile([], []); | |
}; | |
function d3_scale_quantile(domain, range) { | |
var thresholds; | |
function rescale() { | |
var k = 0, q = range.length; | |
thresholds = []; | |
while (++k < q) thresholds[k - 1] = d3.quantile(domain, k / q); | |
return scale; | |
} | |
function scale(x) { | |
if (!isNaN(x = +x)) return range[d3.bisect(thresholds, x)]; | |
} | |
scale.domain = function(x) { | |
if (!arguments.length) return domain; | |
domain = x.map(d3_number).filter(d3_numeric).sort(d3_ascending); | |
return rescale(); | |
}; | |
scale.range = function(x) { | |
if (!arguments.length) return range; | |
range = x; | |
return rescale(); | |
}; | |
scale.quantiles = function() { | |
return thresholds; | |
}; | |
scale.invertExtent = function(y) { | |
y = range.indexOf(y); | |
return y < 0 ? [ NaN, NaN ] : [ y > 0 ? thresholds[y - 1] : domain[0], y < thresholds.length ? thresholds[y] : domain[domain.length - 1] ]; | |
}; | |
scale.copy = function() { | |
return d3_scale_quantile(domain, range); | |
}; | |
return rescale(); | |
} | |
d3.scale.quantize = function() { | |
return d3_scale_quantize(0, 1, [ 0, 1 ]); | |
}; | |
function d3_scale_quantize(x0, x1, range) { | |
var kx, i; | |
function scale(x) { | |
return range[Math.max(0, Math.min(i, Math.floor(kx * (x - x0))))]; | |
} | |
function rescale() { | |
kx = range.length / (x1 - x0); | |
i = range.length - 1; | |
return scale; | |
} | |
scale.domain = function(x) { | |
if (!arguments.length) return [ x0, x1 ]; | |
x0 = +x[0]; | |
x1 = +x[x.length - 1]; | |
return rescale(); | |
}; | |
scale.range = function(x) { | |
if (!arguments.length) return range; | |
range = x; | |
return rescale(); | |
}; | |
scale.invertExtent = function(y) { | |
y = range.indexOf(y); | |
y = y < 0 ? NaN : y / kx + x0; | |
return [ y, y + 1 / kx ]; | |
}; | |
scale.copy = function() { | |
return d3_scale_quantize(x0, x1, range); | |
}; | |
return rescale(); | |
} | |
d3.scale.threshold = function() { | |
return d3_scale_threshold([ .5 ], [ 0, 1 ]); | |
}; | |
function d3_scale_threshold(domain, range) { | |
function scale(x) { | |
if (x <= x) return range[d3.bisect(domain, x)]; | |
} | |
scale.domain = function(_) { | |
if (!arguments.length) return domain; | |
domain = _; | |
return scale; | |
}; | |
scale.range = function(_) { | |
if (!arguments.length) return range; | |
range = _; | |
return scale; | |
}; | |
scale.invertExtent = function(y) { | |
y = range.indexOf(y); | |
return [ domain[y - 1], domain[y] ]; | |
}; | |
scale.copy = function() { | |
return d3_scale_threshold(domain, range); | |
}; | |
return scale; | |
} | |
d3.scale.identity = function() { | |
return d3_scale_identity([ 0, 1 ]); | |
}; | |
function d3_scale_identity(domain) { | |
function identity(x) { | |
return +x; | |
} | |
identity.invert = identity; | |
identity.domain = identity.range = function(x) { | |
if (!arguments.length) return domain; | |
domain = x.map(identity); | |
return identity; | |
}; | |
identity.ticks = function(m) { | |
return d3_scale_linearTicks(domain, m); | |
}; | |
identity.tickFormat = function(m, format) { | |
return d3_scale_linearTickFormat(domain, m, format); | |
}; | |
identity.copy = function() { | |
return d3_scale_identity(domain); | |
}; | |
return identity; | |
} | |
d3.svg = {}; | |
function d3_zero() { | |
return 0; | |
} | |
d3.svg.arc = function() { | |
var innerRadius = d3_svg_arcInnerRadius, outerRadius = d3_svg_arcOuterRadius, cornerRadius = d3_zero, padRadius = d3_svg_arcAuto, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle, padAngle = d3_svg_arcPadAngle; | |
function arc() { | |
var r0 = Math.max(0, +innerRadius.apply(this, arguments)), r1 = Math.max(0, +outerRadius.apply(this, arguments)), a0 = startAngle.apply(this, arguments) - halfπ, a1 = endAngle.apply(this, arguments) - halfπ, da = Math.abs(a1 - a0), cw = a0 > a1 ? 0 : 1; | |
if (r1 < r0) rc = r1, r1 = r0, r0 = rc; | |
if (da >= τε) return circleSegment(r1, cw) + (r0 ? circleSegment(r0, 1 - cw) : "") + "Z"; | |
var rc, cr, rp, ap, p0 = 0, p1 = 0, x0, y0, x1, y1, x2, y2, x3, y3, path = []; | |
if (ap = (+padAngle.apply(this, arguments) || 0) / 2) { | |
rp = padRadius === d3_svg_arcAuto ? Math.sqrt(r0 * r0 + r1 * r1) : +padRadius.apply(this, arguments); | |
if (!cw) p1 *= -1; | |
if (r1) p1 = d3_asin(rp / r1 * Math.sin(ap)); | |
if (r0) p0 = d3_asin(rp / r0 * Math.sin(ap)); | |
} | |
if (r1) { | |
x0 = r1 * Math.cos(a0 + p1); | |
y0 = r1 * Math.sin(a0 + p1); | |
x1 = r1 * Math.cos(a1 - p1); | |
y1 = r1 * Math.sin(a1 - p1); | |
var l1 = Math.abs(a1 - a0 - 2 * p1) <= π ? 0 : 1; | |
if (p1 && d3_svg_arcSweep(x0, y0, x1, y1) === cw ^ l1) { | |
var h1 = (a0 + a1) / 2; | |
x0 = r1 * Math.cos(h1); | |
y0 = r1 * Math.sin(h1); | |
x1 = y1 = null; | |
} | |
} else { | |
x0 = y0 = 0; | |
} | |
if (r0) { | |
x2 = r0 * Math.cos(a1 - p0); | |
y2 = r0 * Math.sin(a1 - p0); | |
x3 = r0 * Math.cos(a0 + p0); | |
y3 = r0 * Math.sin(a0 + p0); | |
var l0 = Math.abs(a0 - a1 + 2 * p0) <= π ? 0 : 1; | |
if (p0 && d3_svg_arcSweep(x2, y2, x3, y3) === 1 - cw ^ l0) { | |
var h0 = (a0 + a1) / 2; | |
x2 = r0 * Math.cos(h0); | |
y2 = r0 * Math.sin(h0); | |
x3 = y3 = null; | |
} | |
} else { | |
x2 = y2 = 0; | |
} | |
if ((rc = Math.min(Math.abs(r1 - r0) / 2, +cornerRadius.apply(this, arguments))) > .001) { | |
cr = r0 < r1 ^ cw ? 0 : 1; | |
var oc = x3 == null ? [ x2, y2 ] : x1 == null ? [ x0, y0 ] : d3_geom_polygonIntersect([ x0, y0 ], [ x3, y3 ], [ x1, y1 ], [ x2, y2 ]), ax = x0 - oc[0], ay = y0 - oc[1], bx = x1 - oc[0], by = y1 - oc[1], kc = 1 / Math.sin(Math.acos((ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by))) / 2), lc = Math.sqrt(oc[0] * oc[0] + oc[1] * oc[1]); | |
if (x1 != null) { | |
var rc1 = Math.min(rc, (r1 - lc) / (kc + 1)), t30 = d3_svg_arcCornerTangents(x3 == null ? [ x2, y2 ] : [ x3, y3 ], [ x0, y0 ], r1, rc1, cw), t12 = d3_svg_arcCornerTangents([ x1, y1 ], [ x2, y2 ], r1, rc1, cw); | |
if (rc === rc1) { | |
path.push("M", t30[0], "A", rc1, ",", rc1, " 0 0,", cr, " ", t30[1], "A", r1, ",", r1, " 0 ", 1 - cw ^ d3_svg_arcSweep(t30[1][0], t30[1][1], t12[1][0], t12[1][1]), ",", cw, " ", t12[1], "A", rc1, ",", rc1, " 0 0,", cr, " ", t12[0]); | |
} else { | |
path.push("M", t30[0], "A", rc1, ",", rc1, " 0 1,", cr, " ", t12[0]); | |
} | |
} else { | |
path.push("M", x0, ",", y0); | |
} | |
if (x3 != null) { | |
var rc0 = Math.min(rc, (r0 - lc) / (kc - 1)), t03 = d3_svg_arcCornerTangents([ x0, y0 ], [ x3, y3 ], r0, -rc0, cw), t21 = d3_svg_arcCornerTangents([ x2, y2 ], x1 == null ? [ x0, y0 ] : [ x1, y1 ], r0, -rc0, cw); | |
if (rc === rc0) { | |
path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t21[1], "A", r0, ",", r0, " 0 ", cw ^ d3_svg_arcSweep(t21[1][0], t21[1][1], t03[1][0], t03[1][1]), ",", 1 - cw, " ", t03[1], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); | |
} else { | |
path.push("L", t21[0], "A", rc0, ",", rc0, " 0 0,", cr, " ", t03[0]); | |
} | |
} else { | |
path.push("L", x2, ",", y2); | |
} | |
} else { | |
path.push("M", x0, ",", y0); | |
if (x1 != null) path.push("A", r1, ",", r1, " 0 ", l1, ",", cw, " ", x1, ",", y1); | |
path.push("L", x2, ",", y2); | |
if (x3 != null) path.push("A", r0, ",", r0, " 0 ", l0, ",", 1 - cw, " ", x3, ",", y3); | |
} | |
path.push("Z"); | |
return path.join(""); | |
} | |
function circleSegment(r1, cw) { | |
return "M0," + r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + -r1 + "A" + r1 + "," + r1 + " 0 1," + cw + " 0," + r1; | |
} | |
arc.innerRadius = function(v) { | |
if (!arguments.length) return innerRadius; | |
innerRadius = d3_functor(v); | |
return arc; | |
}; | |
arc.outerRadius = function(v) { | |
if (!arguments.length) return outerRadius; | |
outerRadius = d3_functor(v); | |
return arc; | |
}; | |
arc.cornerRadius = function(v) { | |
if (!arguments.length) return cornerRadius; | |
cornerRadius = d3_functor(v); | |
return arc; | |
}; | |
arc.padRadius = function(v) { | |
if (!arguments.length) return padRadius; | |
padRadius = v == d3_svg_arcAuto ? d3_svg_arcAuto : d3_functor(v); | |
return arc; | |
}; | |
arc.startAngle = function(v) { | |
if (!arguments.length) return startAngle; | |
startAngle = d3_functor(v); | |
return arc; | |
}; | |
arc.endAngle = function(v) { | |
if (!arguments.length) return endAngle; | |
endAngle = d3_functor(v); | |
return arc; | |
}; | |
arc.padAngle = function(v) { | |
if (!arguments.length) return padAngle; | |
padAngle = d3_functor(v); | |
return arc; | |
}; | |
arc.centroid = function() { | |
var r = (+innerRadius.apply(this, arguments) + +outerRadius.apply(this, arguments)) / 2, a = (+startAngle.apply(this, arguments) + +endAngle.apply(this, arguments)) / 2 - halfπ; | |
return [ Math.cos(a) * r, Math.sin(a) * r ]; | |
}; | |
return arc; | |
}; | |
var d3_svg_arcAuto = "auto"; | |
function d3_svg_arcInnerRadius(d) { | |
return d.innerRadius; | |
} | |
function d3_svg_arcOuterRadius(d) { | |
return d.outerRadius; | |
} | |
function d3_svg_arcStartAngle(d) { | |
return d.startAngle; | |
} | |
function d3_svg_arcEndAngle(d) { | |
return d.endAngle; | |
} | |
function d3_svg_arcPadAngle(d) { | |
return d && d.padAngle; | |
} | |
function d3_svg_arcSweep(x0, y0, x1, y1) { | |
return (x0 - x1) * y0 - (y0 - y1) * x0 > 0 ? 0 : 1; | |
} | |
function d3_svg_arcCornerTangents(p0, p1, r1, rc, cw) { | |
var x01 = p0[0] - p1[0], y01 = p0[1] - p1[1], lo = (cw ? rc : -rc) / Math.sqrt(x01 * x01 + y01 * y01), ox = lo * y01, oy = -lo * x01, x1 = p0[0] + ox, y1 = p0[1] + oy, x2 = p1[0] + ox, y2 = p1[1] + oy, x3 = (x1 + x2) / 2, y3 = (y1 + y2) / 2, dx = x2 - x1, dy = y2 - y1, d2 = dx * dx + dy * dy, r = r1 - rc, D = x1 * y2 - x2 * y1, d = (dy < 0 ? -1 : 1) * Math.sqrt(r * r * d2 - D * D), cx0 = (D * dy - dx * d) / d2, cy0 = (-D * dx - dy * d) / d2, cx1 = (D * dy + dx * d) / d2, cy1 = (-D * dx + dy * d) / d2, dx0 = cx0 - x3, dy0 = cy0 - y3, dx1 = cx1 - x3, dy1 = cy1 - y3; | |
if (dx0 * dx0 + dy0 * dy0 > dx1 * dx1 + dy1 * dy1) cx0 = cx1, cy0 = cy1; | |
return [ [ cx0 - ox, cy0 - oy ], [ cx0 * r1 / r, cy0 * r1 / r ] ]; | |
} | |
function d3_svg_line(projection) { | |
var x = d3_geom_pointX, y = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, tension = .7; | |
function line(data) { | |
var segments = [], points = [], i = -1, n = data.length, d, fx = d3_functor(x), fy = d3_functor(y); | |
function segment() { | |
segments.push("M", interpolate(projection(points), tension)); | |
} | |
while (++i < n) { | |
if (defined.call(this, d = data[i], i)) { | |
points.push([ +fx.call(this, d, i), +fy.call(this, d, i) ]); | |
} else if (points.length) { | |
segment(); | |
points = []; | |
} | |
} | |
if (points.length) segment(); | |
return segments.length ? segments.join("") : null; | |
} | |
line.x = function(_) { | |
if (!arguments.length) return x; | |
x = _; | |
return line; | |
}; | |
line.y = function(_) { | |
if (!arguments.length) return y; | |
y = _; | |
return line; | |
}; | |
line.defined = function(_) { | |
if (!arguments.length) return defined; | |
defined = _; | |
return line; | |
}; | |
line.interpolate = function(_) { | |
if (!arguments.length) return interpolateKey; | |
if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; | |
return line; | |
}; | |
line.tension = function(_) { | |
if (!arguments.length) return tension; | |
tension = _; | |
return line; | |
}; | |
return line; | |
} | |
d3.svg.line = function() { | |
return d3_svg_line(d3_identity); | |
}; | |
var d3_svg_lineInterpolators = d3.map({ | |
linear: d3_svg_lineLinear, | |
"linear-closed": d3_svg_lineLinearClosed, | |
step: d3_svg_lineStep, | |
"step-before": d3_svg_lineStepBefore, | |
"step-after": d3_svg_lineStepAfter, | |
basis: d3_svg_lineBasis, | |
"basis-open": d3_svg_lineBasisOpen, | |
"basis-closed": d3_svg_lineBasisClosed, | |
bundle: d3_svg_lineBundle, | |
cardinal: d3_svg_lineCardinal, | |
"cardinal-open": d3_svg_lineCardinalOpen, | |
"cardinal-closed": d3_svg_lineCardinalClosed, | |
monotone: d3_svg_lineMonotone | |
}); | |
d3_svg_lineInterpolators.forEach(function(key, value) { | |
value.key = key; | |
value.closed = /-closed$/.test(key); | |
}); | |
function d3_svg_lineLinear(points) { | |
return points.join("L"); | |
} | |
function d3_svg_lineLinearClosed(points) { | |
return d3_svg_lineLinear(points) + "Z"; | |
} | |
function d3_svg_lineStep(points) { | |
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; | |
while (++i < n) path.push("H", (p[0] + (p = points[i])[0]) / 2, "V", p[1]); | |
if (n > 1) path.push("H", p[0]); | |
return path.join(""); | |
} | |
function d3_svg_lineStepBefore(points) { | |
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; | |
while (++i < n) path.push("V", (p = points[i])[1], "H", p[0]); | |
return path.join(""); | |
} | |
function d3_svg_lineStepAfter(points) { | |
var i = 0, n = points.length, p = points[0], path = [ p[0], ",", p[1] ]; | |
while (++i < n) path.push("H", (p = points[i])[0], "V", p[1]); | |
return path.join(""); | |
} | |
function d3_svg_lineCardinalOpen(points, tension) { | |
return points.length < 4 ? d3_svg_lineLinear(points) : points[1] + d3_svg_lineHermite(points.slice(1, -1), d3_svg_lineCardinalTangents(points, tension)); | |
} | |
function d3_svg_lineCardinalClosed(points, tension) { | |
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite((points.push(points[0]), | |
points), d3_svg_lineCardinalTangents([ points[points.length - 2] ].concat(points, [ points[1] ]), tension)); | |
} | |
function d3_svg_lineCardinal(points, tension) { | |
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineCardinalTangents(points, tension)); | |
} | |
function d3_svg_lineHermite(points, tangents) { | |
if (tangents.length < 1 || points.length != tangents.length && points.length != tangents.length + 2) { | |
return d3_svg_lineLinear(points); | |
} | |
var quad = points.length != tangents.length, path = "", p0 = points[0], p = points[1], t0 = tangents[0], t = t0, pi = 1; | |
if (quad) { | |
path += "Q" + (p[0] - t0[0] * 2 / 3) + "," + (p[1] - t0[1] * 2 / 3) + "," + p[0] + "," + p[1]; | |
p0 = points[1]; | |
pi = 2; | |
} | |
if (tangents.length > 1) { | |
t = tangents[1]; | |
p = points[pi]; | |
pi++; | |
path += "C" + (p0[0] + t0[0]) + "," + (p0[1] + t0[1]) + "," + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; | |
for (var i = 2; i < tangents.length; i++, pi++) { | |
p = points[pi]; | |
t = tangents[i]; | |
path += "S" + (p[0] - t[0]) + "," + (p[1] - t[1]) + "," + p[0] + "," + p[1]; | |
} | |
} | |
if (quad) { | |
var lp = points[pi]; | |
path += "Q" + (p[0] + t[0] * 2 / 3) + "," + (p[1] + t[1] * 2 / 3) + "," + lp[0] + "," + lp[1]; | |
} | |
return path; | |
} | |
function d3_svg_lineCardinalTangents(points, tension) { | |
var tangents = [], a = (1 - tension) / 2, p0, p1 = points[0], p2 = points[1], i = 1, n = points.length; | |
while (++i < n) { | |
p0 = p1; | |
p1 = p2; | |
p2 = points[i]; | |
tangents.push([ a * (p2[0] - p0[0]), a * (p2[1] - p0[1]) ]); | |
} | |
return tangents; | |
} | |
function d3_svg_lineBasis(points) { | |
if (points.length < 3) return d3_svg_lineLinear(points); | |
var i = 1, n = points.length, pi = points[0], x0 = pi[0], y0 = pi[1], px = [ x0, x0, x0, (pi = points[1])[0] ], py = [ y0, y0, y0, pi[1] ], path = [ x0, ",", y0, "L", d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; | |
points.push(points[n - 1]); | |
while (++i <= n) { | |
pi = points[i]; | |
px.shift(); | |
px.push(pi[0]); | |
py.shift(); | |
py.push(pi[1]); | |
d3_svg_lineBasisBezier(path, px, py); | |
} | |
points.pop(); | |
path.push("L", pi); | |
return path.join(""); | |
} | |
function d3_svg_lineBasisOpen(points) { | |
if (points.length < 4) return d3_svg_lineLinear(points); | |
var path = [], i = -1, n = points.length, pi, px = [ 0 ], py = [ 0 ]; | |
while (++i < 3) { | |
pi = points[i]; | |
px.push(pi[0]); | |
py.push(pi[1]); | |
} | |
path.push(d3_svg_lineDot4(d3_svg_lineBasisBezier3, px) + "," + d3_svg_lineDot4(d3_svg_lineBasisBezier3, py)); | |
--i; | |
while (++i < n) { | |
pi = points[i]; | |
px.shift(); | |
px.push(pi[0]); | |
py.shift(); | |
py.push(pi[1]); | |
d3_svg_lineBasisBezier(path, px, py); | |
} | |
return path.join(""); | |
} | |
function d3_svg_lineBasisClosed(points) { | |
var path, i = -1, n = points.length, m = n + 4, pi, px = [], py = []; | |
while (++i < 4) { | |
pi = points[i % n]; | |
px.push(pi[0]); | |
py.push(pi[1]); | |
} | |
path = [ d3_svg_lineDot4(d3_svg_lineBasisBezier3, px), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, py) ]; | |
--i; | |
while (++i < m) { | |
pi = points[i % n]; | |
px.shift(); | |
px.push(pi[0]); | |
py.shift(); | |
py.push(pi[1]); | |
d3_svg_lineBasisBezier(path, px, py); | |
} | |
return path.join(""); | |
} | |
function d3_svg_lineBundle(points, tension) { | |
var n = points.length - 1; | |
if (n) { | |
var x0 = points[0][0], y0 = points[0][1], dx = points[n][0] - x0, dy = points[n][1] - y0, i = -1, p, t; | |
while (++i <= n) { | |
p = points[i]; | |
t = i / n; | |
p[0] = tension * p[0] + (1 - tension) * (x0 + t * dx); | |
p[1] = tension * p[1] + (1 - tension) * (y0 + t * dy); | |
} | |
} | |
return d3_svg_lineBasis(points); | |
} | |
function d3_svg_lineDot4(a, b) { | |
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3]; | |
} | |
var d3_svg_lineBasisBezier1 = [ 0, 2 / 3, 1 / 3, 0 ], d3_svg_lineBasisBezier2 = [ 0, 1 / 3, 2 / 3, 0 ], d3_svg_lineBasisBezier3 = [ 0, 1 / 6, 2 / 3, 1 / 6 ]; | |
function d3_svg_lineBasisBezier(path, x, y) { | |
path.push("C", d3_svg_lineDot4(d3_svg_lineBasisBezier1, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier1, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier2, y), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, x), ",", d3_svg_lineDot4(d3_svg_lineBasisBezier3, y)); | |
} | |
function d3_svg_lineSlope(p0, p1) { | |
return (p1[1] - p0[1]) / (p1[0] - p0[0]); | |
} | |
function d3_svg_lineFiniteDifferences(points) { | |
var i = 0, j = points.length - 1, m = [], p0 = points[0], p1 = points[1], d = m[0] = d3_svg_lineSlope(p0, p1); | |
while (++i < j) { | |
m[i] = (d + (d = d3_svg_lineSlope(p0 = p1, p1 = points[i + 1]))) / 2; | |
} | |
m[i] = d; | |
return m; | |
} | |
function d3_svg_lineMonotoneTangents(points) { | |
var tangents = [], d, a, b, s, m = d3_svg_lineFiniteDifferences(points), i = -1, j = points.length - 1; | |
while (++i < j) { | |
d = d3_svg_lineSlope(points[i], points[i + 1]); | |
if (abs(d) < ε) { | |
m[i] = m[i + 1] = 0; | |
} else { | |
a = m[i] / d; | |
b = m[i + 1] / d; | |
s = a * a + b * b; | |
if (s > 9) { | |
s = d * 3 / Math.sqrt(s); | |
m[i] = s * a; | |
m[i + 1] = s * b; | |
} | |
} | |
} | |
i = -1; | |
while (++i <= j) { | |
s = (points[Math.min(j, i + 1)][0] - points[Math.max(0, i - 1)][0]) / (6 * (1 + m[i] * m[i])); | |
tangents.push([ s || 0, m[i] * s || 0 ]); | |
} | |
return tangents; | |
} | |
function d3_svg_lineMonotone(points) { | |
return points.length < 3 ? d3_svg_lineLinear(points) : points[0] + d3_svg_lineHermite(points, d3_svg_lineMonotoneTangents(points)); | |
} | |
d3.svg.line.radial = function() { | |
var line = d3_svg_line(d3_svg_lineRadial); | |
line.radius = line.x, delete line.x; | |
line.angle = line.y, delete line.y; | |
return line; | |
}; | |
function d3_svg_lineRadial(points) { | |
var point, i = -1, n = points.length, r, a; | |
while (++i < n) { | |
point = points[i]; | |
r = point[0]; | |
a = point[1] - halfπ; | |
point[0] = r * Math.cos(a); | |
point[1] = r * Math.sin(a); | |
} | |
return points; | |
} | |
function d3_svg_area(projection) { | |
var x0 = d3_geom_pointX, x1 = d3_geom_pointX, y0 = 0, y1 = d3_geom_pointY, defined = d3_true, interpolate = d3_svg_lineLinear, interpolateKey = interpolate.key, interpolateReverse = interpolate, L = "L", tension = .7; | |
function area(data) { | |
var segments = [], points0 = [], points1 = [], i = -1, n = data.length, d, fx0 = d3_functor(x0), fy0 = d3_functor(y0), fx1 = x0 === x1 ? function() { | |
return x; | |
} : d3_functor(x1), fy1 = y0 === y1 ? function() { | |
return y; | |
} : d3_functor(y1), x, y; | |
function segment() { | |
segments.push("M", interpolate(projection(points1), tension), L, interpolateReverse(projection(points0.reverse()), tension), "Z"); | |
} | |
while (++i < n) { | |
if (defined.call(this, d = data[i], i)) { | |
points0.push([ x = +fx0.call(this, d, i), y = +fy0.call(this, d, i) ]); | |
points1.push([ +fx1.call(this, d, i), +fy1.call(this, d, i) ]); | |
} else if (points0.length) { | |
segment(); | |
points0 = []; | |
points1 = []; | |
} | |
} | |
if (points0.length) segment(); | |
return segments.length ? segments.join("") : null; | |
} | |
area.x = function(_) { | |
if (!arguments.length) return x1; | |
x0 = x1 = _; | |
return area; | |
}; | |
area.x0 = function(_) { | |
if (!arguments.length) return x0; | |
x0 = _; | |
return area; | |
}; | |
area.x1 = function(_) { | |
if (!arguments.length) return x1; | |
x1 = _; | |
return area; | |
}; | |
area.y = function(_) { | |
if (!arguments.length) return y1; | |
y0 = y1 = _; | |
return area; | |
}; | |
area.y0 = function(_) { | |
if (!arguments.length) return y0; | |
y0 = _; | |
return area; | |
}; | |
area.y1 = function(_) { | |
if (!arguments.length) return y1; | |
y1 = _; | |
return area; | |
}; | |
area.defined = function(_) { | |
if (!arguments.length) return defined; | |
defined = _; | |
return area; | |
}; | |
area.interpolate = function(_) { | |
if (!arguments.length) return interpolateKey; | |
if (typeof _ === "function") interpolateKey = interpolate = _; else interpolateKey = (interpolate = d3_svg_lineInterpolators.get(_) || d3_svg_lineLinear).key; | |
interpolateReverse = interpolate.reverse || interpolate; | |
L = interpolate.closed ? "M" : "L"; | |
return area; | |
}; | |
area.tension = function(_) { | |
if (!arguments.length) return tension; | |
tension = _; | |
return area; | |
}; | |
return area; | |
} | |
d3_svg_lineStepBefore.reverse = d3_svg_lineStepAfter; | |
d3_svg_lineStepAfter.reverse = d3_svg_lineStepBefore; | |
d3.svg.area = function() { | |
return d3_svg_area(d3_identity); | |
}; | |
d3.svg.area.radial = function() { | |
var area = d3_svg_area(d3_svg_lineRadial); | |
area.radius = area.x, delete area.x; | |
area.innerRadius = area.x0, delete area.x0; | |
area.outerRadius = area.x1, delete area.x1; | |
area.angle = area.y, delete area.y; | |
area.startAngle = area.y0, delete area.y0; | |
area.endAngle = area.y1, delete area.y1; | |
return area; | |
}; | |
d3.svg.chord = function() { | |
var source = d3_source, target = d3_target, radius = d3_svg_chordRadius, startAngle = d3_svg_arcStartAngle, endAngle = d3_svg_arcEndAngle; | |
function chord(d, i) { | |
var s = subgroup(this, source, d, i), t = subgroup(this, target, d, i); | |
return "M" + s.p0 + arc(s.r, s.p1, s.a1 - s.a0) + (equals(s, t) ? curve(s.r, s.p1, s.r, s.p0) : curve(s.r, s.p1, t.r, t.p0) + arc(t.r, t.p1, t.a1 - t.a0) + curve(t.r, t.p1, s.r, s.p0)) + "Z"; | |
} | |
function subgroup(self, f, d, i) { | |
var subgroup = f.call(self, d, i), r = radius.call(self, subgroup, i), a0 = startAngle.call(self, subgroup, i) - halfπ, a1 = endAngle.call(self, subgroup, i) - halfπ; | |
return { | |
r: r, | |
a0: a0, | |
a1: a1, | |
p0: [ r * Math.cos(a0), r * Math.sin(a0) ], | |
p1: [ r * Math.cos(a1), r * Math.sin(a1) ] | |
}; | |
} | |
function equals(a, b) { | |
return a.a0 == b.a0 && a.a1 == b.a1; | |
} | |
function arc(r, p, a) { | |
return "A" + r + "," + r + " 0 " + +(a > π) + ",1 " + p; | |
} | |
function curve(r0, p0, r1, p1) { | |
return "Q 0,0 " + p1; | |
} | |
chord.radius = function(v) { | |
if (!arguments.length) return radius; | |
radius = d3_functor(v); | |
return chord; | |
}; | |
chord.source = function(v) { | |
if (!arguments.length) return source; | |
source = d3_functor(v); | |
return chord; | |
}; | |
chord.target = function(v) { | |
if (!arguments.length) return target; | |
target = d3_functor(v); | |
return chord; | |
}; | |
chord.startAngle = function(v) { | |
if (!arguments.length) return startAngle; | |
startAngle = d3_functor(v); | |
return chord; | |
}; | |
chord.endAngle = function(v) { | |
if (!arguments.length) return endAngle; | |
endAngle = d3_functor(v); | |
return chord; | |
}; | |
return chord; | |
}; | |
function d3_svg_chordRadius(d) { | |
return d.radius; | |
} | |
d3.svg.diagonal = function() { | |
var source = d3_source, target = d3_target, projection = d3_svg_diagonalProjection; | |
function diagonal(d, i) { | |
var p0 = source.call(this, d, i), p3 = target.call(this, d, i), m = (p0.y + p3.y) / 2, p = [ p0, { | |
x: p0.x, | |
y: m | |
}, { | |
x: p3.x, | |
y: m | |
}, p3 ]; | |
p = p.map(projection); | |
return "M" + p[0] + "C" + p[1] + " " + p[2] + " " + p[3]; | |
} | |
diagonal.source = function(x) { | |
if (!arguments.length) return source; | |
source = d3_functor(x); | |
return diagonal; | |
}; | |
diagonal.target = function(x) { | |
if (!arguments.length) return target; | |
target = d3_functor(x); | |
return diagonal; | |
}; | |
diagonal.projection = function(x) { | |
if (!arguments.length) return projection; | |
projection = x; | |
return diagonal; | |
}; | |
return diagonal; | |
}; | |
function d3_svg_diagonalProjection(d) { | |
return [ d.x, d.y ]; | |
} | |
d3.svg.diagonal.radial = function() { | |
var diagonal = d3.svg.diagonal(), projection = d3_svg_diagonalProjection, projection_ = diagonal.projection; | |
diagonal.projection = function(x) { | |
return arguments.length ? projection_(d3_svg_diagonalRadialProjection(projection = x)) : projection; | |
}; | |
return diagonal; | |
}; | |
function d3_svg_diagonalRadialProjection(projection) { | |
return function() { | |
var d = projection.apply(this, arguments), r = d[0], a = d[1] - halfπ; | |
return [ r * Math.cos(a), r * Math.sin(a) ]; | |
}; | |
} | |
d3.svg.symbol = function() { | |
var type = d3_svg_symbolType, size = d3_svg_symbolSize; | |
function symbol(d, i) { | |
return (d3_svg_symbols.get(type.call(this, d, i)) || d3_svg_symbolCircle)(size.call(this, d, i)); | |
} | |
symbol.type = function(x) { | |
if (!arguments.length) return type; | |
type = d3_functor(x); | |
return symbol; | |
}; | |
symbol.size = function(x) { | |
if (!arguments.length) return size; | |
size = d3_functor(x); | |
return symbol; | |
}; | |
return symbol; | |
}; | |
function d3_svg_symbolSize() { | |
return 64; | |
} | |
function d3_svg_symbolType() { | |
return "circle"; | |
} | |
function d3_svg_symbolCircle(size) { | |
var r = Math.sqrt(size / π); | |
return "M0," + r + "A" + r + "," + r + " 0 1,1 0," + -r + "A" + r + "," + r + " 0 1,1 0," + r + "Z"; | |
} | |
var d3_svg_symbols = d3.map({ | |
circle: d3_svg_symbolCircle, | |
cross: function(size) { | |
var r = Math.sqrt(size / 5) / 2; | |
return "M" + -3 * r + "," + -r + "H" + -r + "V" + -3 * r + "H" + r + "V" + -r + "H" + 3 * r + "V" + r + "H" + r + "V" + 3 * r + "H" + -r + "V" + r + "H" + -3 * r + "Z"; | |
}, | |
diamond: function(size) { | |
var ry = Math.sqrt(size / (2 * d3_svg_symbolTan30)), rx = ry * d3_svg_symbolTan30; | |
return "M0," + -ry + "L" + rx + ",0" + " 0," + ry + " " + -rx + ",0" + "Z"; | |
}, | |
square: function(size) { | |
var r = Math.sqrt(size) / 2; | |
return "M" + -r + "," + -r + "L" + r + "," + -r + " " + r + "," + r + " " + -r + "," + r + "Z"; | |
}, | |
"triangle-down": function(size) { | |
var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; | |
return "M0," + ry + "L" + rx + "," + -ry + " " + -rx + "," + -ry + "Z"; | |
}, | |
"triangle-up": function(size) { | |
var rx = Math.sqrt(size / d3_svg_symbolSqrt3), ry = rx * d3_svg_symbolSqrt3 / 2; | |
return "M0," + -ry + "L" + rx + "," + ry + " " + -rx + "," + ry + "Z"; | |
} | |
}); | |
d3.svg.symbolTypes = d3_svg_symbols.keys(); | |
var d3_svg_symbolSqrt3 = Math.sqrt(3), d3_svg_symbolTan30 = Math.tan(30 * d3_radians); | |
d3_selectionPrototype.transition = function(name) { | |
var id = d3_transitionInheritId || ++d3_transitionId, ns = d3_transitionNamespace(name), subgroups = [], subgroup, node, transition = d3_transitionInherit || { | |
time: Date.now(), | |
ease: d3_ease_cubicInOut, | |
delay: 0, | |
duration: 250 | |
}; | |
for (var j = -1, m = this.length; ++j < m; ) { | |
subgroups.push(subgroup = []); | |
for (var group = this[j], i = -1, n = group.length; ++i < n; ) { | |
if (node = group[i]) d3_transitionNode(node, i, ns, id, transition); | |
subgroup.push(node); | |
} | |
} | |
return d3_transition(subgroups, ns, id); | |
}; | |
d3_selectionPrototype.interrupt = function(name) { | |
return this.each(name == null ? d3_selection_interrupt : d3_selection_interruptNS(d3_transitionNamespace(name))); | |
}; | |
var d3_selection_interrupt = d3_selection_interruptNS(d3_transitionNamespace()); | |
function d3_selection_interruptNS(ns) { | |
return function() { | |
var lock, active; | |
if ((lock = this[ns]) && (active = lock[lock.active])) { | |
if (--lock.count) delete lock[lock.active]; else delete this[ns]; | |
lock.active += .5; | |
active.event && active.event.interrupt.call(this, this.__data__, active.index); | |
} | |
}; | |
} | |
function d3_transition(groups, ns, id) { | |
d3_subclass(groups, d3_transitionPrototype); | |
groups.namespace = ns; | |
groups.id = id; | |
return groups; | |
} | |
var d3_transitionPrototype = [], d3_transitionId = 0, d3_transitionInheritId, d3_transitionInherit; | |
d3_transitionPrototype.call = d3_selectionPrototype.call; | |
d3_transitionPrototype.empty = d3_selectionPrototype.empty; | |
d3_transitionPrototype.node = d3_selectionPrototype.node; | |
d3_transitionPrototype.size = d3_selectionPrototype.size; | |
d3.transition = function(selection, name) { | |
return selection && selection.transition ? d3_transitionInheritId ? selection.transition(name) : selection : d3.selection().transition(selection); | |
}; | |
d3.transition.prototype = d3_transitionPrototype; | |
d3_transitionPrototype.select = function(selector) { | |
var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnode, node; | |
selector = d3_selection_selector(selector); | |
for (var j = -1, m = this.length; ++j < m; ) { | |
subgroups.push(subgroup = []); | |
for (var group = this[j], i = -1, n = group.length; ++i < n; ) { | |
if ((node = group[i]) && (subnode = selector.call(node, node.__data__, i, j))) { | |
if ("__data__" in node) subnode.__data__ = node.__data__; | |
d3_transitionNode(subnode, i, ns, id, node[ns][id]); | |
subgroup.push(subnode); | |
} else { | |
subgroup.push(null); | |
} | |
} | |
} | |
return d3_transition(subgroups, ns, id); | |
}; | |
d3_transitionPrototype.selectAll = function(selector) { | |
var id = this.id, ns = this.namespace, subgroups = [], subgroup, subnodes, node, subnode, transition; | |
selector = d3_selection_selectorAll(selector); | |
for (var j = -1, m = this.length; ++j < m; ) { | |
for (var group = this[j], i = -1, n = group.length; ++i < n; ) { | |
if (node = group[i]) { | |
transition = node[ns][id]; | |
subnodes = selector.call(node, node.__data__, i, j); | |
subgroups.push(subgroup = []); | |
for (var k = -1, o = subnodes.length; ++k < o; ) { | |
if (subnode = subnodes[k]) d3_transitionNode(subnode, k, ns, id, transition); | |
subgroup.push(subnode); | |
} | |
} | |
} | |
} | |
return d3_transition(subgroups, ns, id); | |
}; | |
d3_transitionPrototype.filter = function(filter) { | |
var subgroups = [], subgroup, group, node; | |
if (typeof filter !== "function") filter = d3_selection_filter(filter); | |
for (var j = 0, m = this.length; j < m; j++) { | |
subgroups.push(subgroup = []); | |
for (var group = this[j], i = 0, n = group.length; i < n; i++) { | |
if ((node = group[i]) && filter.call(node, node.__data__, i, j)) { | |
subgroup.push(node); | |
} | |
} | |
} | |
return d3_transition(subgroups, this.namespace, this.id); | |
}; | |
d3_transitionPrototype.tween = function(name, tween) { | |
var id = this.id, ns = this.namespace; | |
if (arguments.length < 2) return this.node()[ns][id].tween.get(name); | |
return d3_selection_each(this, tween == null ? function(node) { | |
node[ns][id].tween.remove(name); | |
} : function(node) { | |
node[ns][id].tween.set(name, tween); | |
}); | |
}; | |
function d3_transition_tween(groups, name, value, tween) { | |
var id = groups.id, ns = groups.namespace; | |
return d3_selection_each(groups, typeof value === "function" ? function(node, i, j) { | |
node[ns][id].tween.set(name, tween(value.call(node, node.__data__, i, j))); | |
} : (value = tween(value), function(node) { | |
node[ns][id].tween.set(name, value); | |
})); | |
} | |
d3_transitionPrototype.attr = function(nameNS, value) { | |
if (arguments.length < 2) { | |
for (value in nameNS) this.attr(value, nameNS[value]); | |
return this; | |
} | |
var interpolate = nameNS == "transform" ? d3_interpolateTransform : d3_interpolate, name = d3.ns.qualify(nameNS); | |
function attrNull() { | |
this.removeAttribute(name); | |
} | |
function attrNullNS() { | |
this.removeAttributeNS(name.space, name.local); | |
} | |
function attrTween(b) { | |
return b == null ? attrNull : (b += "", function() { | |
var a = this.getAttribute(name), i; | |
return a !== b && (i = interpolate(a, b), function(t) { | |
this.setAttribute(name, i(t)); | |
}); | |
}); | |
} | |
function attrTweenNS(b) { | |
return b == null ? attrNullNS : (b += "", function() { | |
var a = this.getAttributeNS(name.space, name.local), i; | |
return a !== b && (i = interpolate(a, b), function(t) { | |
this.setAttributeNS(name.space, name.local, i(t)); | |
}); | |
}); | |
} | |
return d3_transition_tween(this, "attr." + nameNS, value, name.local ? attrTweenNS : attrTween); | |
}; | |
d3_transitionPrototype.attrTween = function(nameNS, tween) { | |
var name = d3.ns.qualify(nameNS); | |
function attrTween(d, i) { | |
var f = tween.call(this, d, i, this.getAttribute(name)); | |
return f && function(t) { | |
this.setAttribute(name, f(t)); | |
}; | |
} | |
function attrTweenNS(d, i) { | |
var f = tween.call(this, d, i, this.getAttributeNS(name.space, name.local)); | |
return f && function(t) { | |
this.setAttributeNS(name.space, name.local, f(t)); | |
}; | |
} | |
return this.tween("attr." + nameNS, name.local ? attrTweenNS : attrTween); | |
}; | |
d3_transitionPrototype.style = function(name, value, priority) { | |
var n = arguments.length; | |
if (n < 3) { | |
if (typeof name !== "string") { | |
if (n < 2) value = ""; | |
for (priority in name) this.style(priority, name[priority], value); | |
return this; | |
} | |
priority = ""; | |
} | |
function styleNull() { | |
this.style.removeProperty(name); | |
} | |
function styleString(b) { | |
return b == null ? styleNull : (b += "", function() { | |
var a = d3_window(this).getComputedStyle(this, null).getPropertyValue(name), i; | |
return a !== b && (i = d3_interpolate(a, b), function(t) { | |
this.style.setProperty(name, i(t), priority); | |
}); | |
}); | |
} | |
return d3_transition_tween(this, "style." + name, value, styleString); | |
}; | |
d3_transitionPrototype.styleTween = function(name, tween, priority) { | |
if (arguments.length < 3) priority = ""; | |
function styleTween(d, i) { | |
var f = tween.call(this, d, i, d3_window(this).getComputedStyle(this, null).getPropertyValue(name)); | |
return f && function(t) { | |
this.style.setProperty(name, f(t), priority); | |
}; | |
} | |
return this.tween("style." + name, styleTween); | |
}; | |
d3_transitionPrototype.text = function(value) { | |
return d3_transition_tween(this, "text", value, d3_transition_text); | |
}; | |
function d3_transition_text(b) { | |
if (b == null) b = ""; | |
return function() { | |
this.textContent = b; | |
}; | |
} | |
d3_transitionPrototype.remove = function() { | |
var ns = this.namespace; | |
return this.each("end.transition", function() { | |
var p; | |
if (this[ns].count < 2 && (p = this.parentNode)) p.removeChild(this); | |
}); | |
}; | |
d3_transitionPrototype.ease = function(value) { | |
var id = this.id, ns = this.namespace; | |
if (arguments.length < 1) return this.node()[ns][id].ease; | |
if (typeof value !== "function") value = d3.ease.apply(d3, arguments); | |
return d3_selection_each(this, function(node) { | |
node[ns][id].ease = value; | |
}); | |
}; | |
d3_transitionPrototype.delay = function(value) { | |
var id = this.id, ns = this.namespace; | |
if (arguments.length < 1) return this.node()[ns][id].delay; | |
return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { | |
node[ns][id].delay = +value.call(node, node.__data__, i, j); | |
} : (value = +value, function(node) { | |
node[ns][id].delay = value; | |
})); | |
}; | |
d3_transitionPrototype.duration = function(value) { | |
var id = this.id, ns = this.namespace; | |
if (arguments.length < 1) return this.node()[ns][id].duration; | |
return d3_selection_each(this, typeof value === "function" ? function(node, i, j) { | |
node[ns][id].duration = Math.max(1, value.call(node, node.__data__, i, j)); | |
} : (value = Math.max(1, value), function(node) { | |
node[ns][id].duration = value; | |
})); | |
}; | |
d3_transitionPrototype.each = function(type, listener) { | |
var id = this.id, ns = this.namespace; | |
if (arguments.length < 2) { | |
var inherit = d3_transitionInherit, inheritId = d3_transitionInheritId; | |
try { | |
d3_transitionInheritId = id; | |
d3_selection_each(this, function(node, i, j) { | |
d3_transitionInherit = node[ns][id]; | |
type.call(node, node.__data__, i, j); | |
}); | |
} finally { | |
d3_transitionInherit = inherit; | |
d3_transitionInheritId = inheritId; | |
} | |
} else { | |
d3_selection_each(this, function(node) { | |
var transition = node[ns][id]; | |
(transition.event || (transition.event = d3.dispatch("start", "end", "interrupt"))).on(type, listener); | |
}); | |
} | |
return this; | |
}; | |
d3_transitionPrototype.transition = function() { | |
var id0 = this.id, id1 = ++d3_transitionId, ns = this.namespace, subgroups = [], subgroup, group, node, transition; | |
for (var j = 0, m = this.length; j < m; j++) { | |
subgroups.push(subgroup = []); | |
for (var group = this[j], i = 0, n = group.length; i < n; i++) { | |
if (node = group[i]) { | |
transition = node[ns][id0]; | |
d3_transitionNode(node, i, ns, id1, { | |
time: transition.time, | |
ease: transition.ease, | |
delay: transition.delay + transition.duration, | |
duration: transition.duration | |
}); | |
} | |
subgroup.push(node); | |
} | |
} | |
return d3_transition(subgroups, ns, id1); | |
}; | |
function d3_transitionNamespace(name) { | |
return name == null ? "__transition__" : "__transition_" + name + "__"; | |
} | |
function d3_transitionNode(node, i, ns, id, inherit) { | |
var lock = node[ns] || (node[ns] = { | |
active: 0, | |
count: 0 | |
}), transition = lock[id]; | |
if (!transition) { | |
var time = inherit.time; | |
transition = lock[id] = { | |
tween: new d3_Map(), | |
time: time, | |
delay: inherit.delay, | |
duration: inherit.duration, | |
ease: inherit.ease, | |
index: i | |
}; | |
inherit = null; | |
++lock.count; | |
d3.timer(function(elapsed) { | |
var delay = transition.delay, duration, ease, timer = d3_timer_active, tweened = []; | |
timer.t = delay + time; | |
if (delay <= elapsed) return start(elapsed - delay); | |
timer.c = start; | |
function start(elapsed) { | |
if (lock.active > id) return stop(); | |
var active = lock[lock.active]; | |
if (active) { | |
--lock.count; | |
delete lock[lock.active]; | |
active.event && active.event.interrupt.call(node, node.__data__, active.index); | |
} | |
lock.active = id; | |
transition.event && transition.event.start.call(node, node.__data__, i); | |
transition.tween.forEach(function(key, value) { | |
if (value = value.call(node, node.__data__, i)) { | |
tweened.push(value); | |
} | |
}); | |
ease = transition.ease; | |
duration = transition.duration; | |
d3.timer(function() { | |
timer.c = tick(elapsed || 1) ? d3_true : tick; | |
return 1; | |
}, 0, time); | |
} | |
function tick(elapsed) { | |
if (lock.active !== id) return 1; | |
var t = elapsed / duration, e = ease(t), n = tweened.length; | |
while (n > 0) { | |
tweened[--n].call(node, e); | |
} | |
if (t >= 1) { | |
transition.event && transition.event.end.call(node, node.__data__, i); | |
return stop(); | |
} | |
} | |
function stop() { | |
if (--lock.count) delete lock[id]; else delete node[ns]; | |
return 1; | |
} | |
}, 0, time); | |
} | |
} | |
d3.svg.axis = function() { | |
var scale = d3.scale.linear(), orient = d3_svg_axisDefaultOrient, innerTickSize = 6, outerTickSize = 6, tickPadding = 3, tickArguments_ = [ 10 ], tickValues = null, tickFormat_; | |
function axis(g) { | |
g.each(function() { | |
var g = d3.select(this); | |
var scale0 = this.__chart__ || scale, scale1 = this.__chart__ = scale.copy(); | |
var ticks = tickValues == null ? scale1.ticks ? scale1.ticks.apply(scale1, tickArguments_) : scale1.domain() : tickValues, tickFormat = tickFormat_ == null ? scale1.tickFormat ? scale1.tickFormat.apply(scale1, tickArguments_) : d3_identity : tickFormat_, tick = g.selectAll(".tick").data(ticks, scale1), tickEnter = tick.enter().insert("g", ".domain").attr("class", "tick").style("opacity", ε), tickExit = d3.transition(tick.exit()).style("opacity", ε).remove(), tickUpdate = d3.transition(tick.order()).style("opacity", 1), tickSpacing = Math.max(innerTickSize, 0) + tickPadding, tickTransform; | |
var range = d3_scaleRange(scale1), path = g.selectAll(".domain").data([ 0 ]), pathUpdate = (path.enter().append("path").attr("class", "domain"), | |
d3.transition(path)); | |
tickEnter.append("line"); | |
tickEnter.append("text"); | |
var lineEnter = tickEnter.select("line"), lineUpdate = tickUpdate.select("line"), text = tick.select("text").text(tickFormat), textEnter = tickEnter.select("text"), textUpdate = tickUpdate.select("text"), sign = orient === "top" || orient === "left" ? -1 : 1, x1, x2, y1, y2; | |
if (orient === "bottom" || orient === "top") { | |
tickTransform = d3_svg_axisX, x1 = "x", y1 = "y", x2 = "x2", y2 = "y2"; | |
text.attr("dy", sign < 0 ? "0em" : ".71em").style("text-anchor", "middle"); | |
pathUpdate.attr("d", "M" + range[0] + "," + sign * outerTickSize + "V0H" + range[1] + "V" + sign * outerTickSize); | |
} else { | |
tickTransform = d3_svg_axisY, x1 = "y", y1 = "x", x2 = "y2", y2 = "x2"; | |
text.attr("dy", ".32em").style("text-anchor", sign < 0 ? "end" : "start"); | |
pathUpdate.attr("d", "M" + sign * outerTickSize + "," + range[0] + "H0V" + range[1] + "H" + sign * outerTickSize); | |
} | |
lineEnter.attr(y2, sign * innerTickSize); | |
textEnter.attr(y1, sign * tickSpacing); | |
lineUpdate.attr(x2, 0).attr(y2, sign * innerTickSize); | |
textUpdate.attr(x1, 0).attr(y1, sign * tickSpacing); | |
if (scale1.rangeBand) { | |
var x = scale1, dx = x.rangeBand() / 2; | |
scale0 = scale1 = function(d) { | |
return x(d) + dx; | |
}; | |
} else if (scale0.rangeBand) { | |
scale0 = scale1; | |
} else { | |
tickExit.call(tickTransform, scale1, scale0); | |
} | |
tickEnter.call(tickTransform, scale0, scale1); | |
tickUpdate.call(tickTransform, scale1, scale1); | |
}); | |
} | |
axis.scale = function(x) { | |
if (!arguments.length) return scale; | |
scale = x; | |
return axis; | |
}; | |
axis.orient = function(x) { | |
if (!arguments.length) return orient; | |
orient = x in d3_svg_axisOrients ? x + "" : d3_svg_axisDefaultOrient; | |
return axis; | |
}; | |
axis.ticks = function() { | |
if (!arguments.length) return tickArguments_; | |
tickArguments_ = arguments; | |
return axis; | |
}; | |
axis.tickValues = function(x) { | |
if (!arguments.length) return tickValues; | |
tickValues = x; | |
return axis; | |
}; | |
axis.tickFormat = function(x) { | |
if (!arguments.length) return tickFormat_; | |
tickFormat_ = x; | |
return axis; | |
}; | |
axis.tickSize = function(x) { | |
var n = arguments.length; | |
if (!n) return innerTickSize; | |
innerTickSize = +x; | |
outerTickSize = +arguments[n - 1]; | |
return axis; | |
}; | |
axis.innerTickSize = function(x) { | |
if (!arguments.length) return innerTickSize; | |
innerTickSize = +x; | |
return axis; | |
}; | |
axis.outerTickSize = function(x) { | |
if (!arguments.length) return outerTickSize; | |
outerTickSize = +x; | |
return axis; | |
}; | |
axis.tickPadding = function(x) { | |
if (!arguments.length) return tickPadding; | |
tickPadding = +x; | |
return axis; | |
}; | |
axis.tickSubdivide = function() { | |
return arguments.length && axis; | |
}; | |
return axis; | |
}; | |
var d3_svg_axisDefaultOrient = "bottom", d3_svg_axisOrients = { | |
top: 1, | |
right: 1, | |
bottom: 1, | |
left: 1 | |
}; | |
function d3_svg_axisX(selection, x0, x1) { | |
selection.attr("transform", function(d) { | |
var v0 = x0(d); | |
return "translate(" + (isFinite(v0) ? v0 : x1(d)) + ",0)"; | |
}); | |
} | |
function d3_svg_axisY(selection, y0, y1) { | |
selection.attr("transform", function(d) { | |
var v0 = y0(d); | |
return "translate(0," + (isFinite(v0) ? v0 : y1(d)) + ")"; | |
}); | |
} | |
d3.svg.brush = function() { | |
var event = d3_eventDispatch(brush, "brushstart", "brush", "brushend"), x = null, y = null, xExtent = [ 0, 0 ], yExtent = [ 0, 0 ], xExtentDomain, yExtentDomain, xClamp = true, yClamp = true, resizes = d3_svg_brushResizes[0]; | |
function brush(g) { | |
g.each(function() { | |
var g = d3.select(this).style("pointer-events", "all").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)").on("mousedown.brush", brushstart).on("touchstart.brush", brushstart); | |
var background = g.selectAll(".background").data([ 0 ]); | |
background.enter().append("rect").attr("class", "background").style("visibility", "hidden").style("cursor", "crosshair"); | |
g.selectAll(".extent").data([ 0 ]).enter().append("rect").attr("class", "extent").style("cursor", "move"); | |
var resize = g.selectAll(".resize").data(resizes, d3_identity); | |
resize.exit().remove(); | |
resize.enter().append("g").attr("class", function(d) { | |
return "resize " + d; | |
}).style("cursor", function(d) { | |
return d3_svg_brushCursor[d]; | |
}).append("rect").attr("x", function(d) { | |
return /[ew]$/.test(d) ? -3 : null; | |
}).attr("y", function(d) { | |
return /^[ns]/.test(d) ? -3 : null; | |
}).attr("width", 6).attr("height", 6).style("visibility", "hidden"); | |
resize.style("display", brush.empty() ? "none" : null); | |
var gUpdate = d3.transition(g), backgroundUpdate = d3.transition(background), range; | |
if (x) { | |
range = d3_scaleRange(x); | |
backgroundUpdate.attr("x", range[0]).attr("width", range[1] - range[0]); | |
redrawX(gUpdate); | |
} | |
if (y) { | |
range = d3_scaleRange(y); | |
backgroundUpdate.attr("y", range[0]).attr("height", range[1] - range[0]); | |
redrawY(gUpdate); | |
} | |
redraw(gUpdate); | |
}); | |
} | |
brush.event = function(g) { | |
g.each(function() { | |
var event_ = event.of(this, arguments), extent1 = { | |
x: xExtent, | |
y: yExtent, | |
i: xExtentDomain, | |
j: yExtentDomain | |
}, extent0 = this.__chart__ || extent1; | |
this.__chart__ = extent1; | |
if (d3_transitionInheritId) { | |
d3.select(this).transition().each("start.brush", function() { | |
xExtentDomain = extent0.i; | |
yExtentDomain = extent0.j; | |
xExtent = extent0.x; | |
yExtent = extent0.y; | |
event_({ | |
type: "brushstart" | |
}); | |
}).tween("brush:brush", function() { | |
var xi = d3_interpolateArray(xExtent, extent1.x), yi = d3_interpolateArray(yExtent, extent1.y); | |
xExtentDomain = yExtentDomain = null; | |
return function(t) { | |
xExtent = extent1.x = xi(t); | |
yExtent = extent1.y = yi(t); | |
event_({ | |
type: "brush", | |
mode: "resize" | |
}); | |
}; | |
}).each("end.brush", function() { | |
xExtentDomain = extent1.i; | |
yExtentDomain = extent1.j; | |
event_({ | |
type: "brush", | |
mode: "resize" | |
}); | |
event_({ | |
type: "brushend" | |
}); | |
}); | |
} else { | |
event_({ | |
type: "brushstart" | |
}); | |
event_({ | |
type: "brush", | |
mode: "resize" | |
}); | |
event_({ | |
type: "brushend" | |
}); | |
} | |
}); | |
}; | |
function redraw(g) { | |
g.selectAll(".resize").attr("transform", function(d) { | |
return "translate(" + xExtent[+/e$/.test(d)] + "," + yExtent[+/^s/.test(d)] + ")"; | |
}); | |
} | |
function redrawX(g) { | |
g.select(".extent").attr("x", xExtent[0]); | |
g.selectAll(".extent,.n>rect,.s>rect").attr("width", xExtent[1] - xExtent[0]); | |
} | |
function redrawY(g) { | |
g.select(".extent").attr("y", yExtent[0]); | |
g.selectAll(".extent,.e>rect,.w>rect").attr("height", yExtent[1] - yExtent[0]); | |
} | |
function brushstart() { | |
var target = this, eventTarget = d3.select(d3.event.target), event_ = event.of(target, arguments), g = d3.select(target), resizing = eventTarget.datum(), resizingX = !/^(n|s)$/.test(resizing) && x, resizingY = !/^(e|w)$/.test(resizing) && y, dragging = eventTarget.classed("extent"), dragRestore = d3_event_dragSuppress(target), center, origin = d3.mouse(target), offset; | |
var w = d3.select(d3_window(target)).on("keydown.brush", keydown).on("keyup.brush", keyup); | |
if (d3.event.changedTouches) { | |
w.on("touchmove.brush", brushmove).on("touchend.brush", brushend); | |
} else { | |
w.on("mousemove.brush", brushmove).on("mouseup.brush", brushend); | |
} | |
g.interrupt().selectAll("*").interrupt(); | |
if (dragging) { | |
origin[0] = xExtent[0] - origin[0]; | |
origin[1] = yExtent[0] - origin[1]; | |
} else if (resizing) { | |
var ex = +/w$/.test(resizing), ey = +/^n/.test(resizing); | |
offset = [ xExtent[1 - ex] - origin[0], yExtent[1 - ey] - origin[1] ]; | |
origin[0] = xExtent[ex]; | |
origin[1] = yExtent[ey]; | |
} else if (d3.event.altKey) center = origin.slice(); | |
g.style("pointer-events", "none").selectAll(".resize").style("display", null); | |
d3.select("body").style("cursor", eventTarget.style("cursor")); | |
event_({ | |
type: "brushstart" | |
}); | |
brushmove(); | |
function keydown() { | |
if (d3.event.keyCode == 32) { | |
if (!dragging) { | |
center = null; | |
origin[0] -= xExtent[1]; | |
origin[1] -= yExtent[1]; | |
dragging = 2; | |
} | |
d3_eventPreventDefault(); | |
} | |
} | |
function keyup() { | |
if (d3.event.keyCode == 32 && dragging == 2) { | |
origin[0] += xExtent[1]; | |
origin[1] += yExtent[1]; | |
dragging = 0; | |
d3_eventPreventDefault(); | |
} | |
} | |
function brushmove() { | |
var point = d3.mouse(target), moved = false; | |
if (offset) { | |
point[0] += offset[0]; | |
point[1] += offset[1]; | |
} | |
if (!dragging) { | |
if (d3.event.altKey) { | |
if (!center) center = [ (xExtent[0] + xExtent[1]) / 2, (yExtent[0] + yExtent[1]) / 2 ]; | |
origin[0] = xExtent[+(point[0] < center[0])]; | |
origin[1] = yExtent[+(point[1] < center[1])]; | |
} else center = null; | |
} | |
if (resizingX && move1(point, x, 0)) { | |
redrawX(g); | |
moved = true; | |
} | |
if (resizingY && move1(point, y, 1)) { | |
redrawY(g); | |
moved = true; | |
} | |
if (moved) { | |
redraw(g); | |
event_({ | |
type: "brush", | |
mode: dragging ? "move" : "resize" | |
}); | |
} | |
} | |
function move1(point, scale, i) { | |
var range = d3_scaleRange(scale), r0 = range[0], r1 = range[1], position = origin[i], extent = i ? yExtent : xExtent, size = extent[1] - extent[0], min, max; | |
if (dragging) { | |
r0 -= position; | |
r1 -= size + position; | |
} | |
min = (i ? yClamp : xClamp) ? Math.max(r0, Math.min(r1, point[i])) : point[i]; | |
if (dragging) { | |
max = (min += position) + size; | |
} else { | |
if (center) position = Math.max(r0, Math.min(r1, 2 * center[i] - min)); | |
if (position < min) { | |
max = min; | |
min = position; | |
} else { | |
max = position; | |
} | |
} | |
if (extent[0] != min || extent[1] != max) { | |
if (i) yExtentDomain = null; else xExtentDomain = null; | |
extent[0] = min; | |
extent[1] = max; | |
return true; | |
} | |
} | |
function brushend() { | |
brushmove(); | |
g.style("pointer-events", "all").selectAll(".resize").style("display", brush.empty() ? "none" : null); | |
d3.select("body").style("cursor", null); | |
w.on("mousemove.brush", null).on("mouseup.brush", null).on("touchmove.brush", null).on("touchend.brush", null).on("keydown.brush", null).on("keyup.brush", null); | |
dragRestore(); | |
event_({ | |
type: "brushend" | |
}); | |
} | |
} | |
brush.x = function(z) { | |
if (!arguments.length) return x; | |
x = z; | |
resizes = d3_svg_brushResizes[!x << 1 | !y]; | |
return brush; | |
}; | |
brush.y = function(z) { | |
if (!arguments.length) return y; | |
y = z; | |
resizes = d3_svg_brushResizes[!x << 1 | !y]; | |
return brush; | |
}; | |
brush.clamp = function(z) { | |
if (!arguments.length) return x && y ? [ xClamp, yClamp ] : x ? xClamp : y ? yClamp : null; | |
if (x && y) xClamp = !!z[0], yClamp = !!z[1]; else if (x) xClamp = !!z; else if (y) yClamp = !!z; | |
return brush; | |
}; | |
brush.extent = function(z) { | |
var x0, x1, y0, y1, t; | |
if (!arguments.length) { | |
if (x) { | |
if (xExtentDomain) { | |
x0 = xExtentDomain[0], x1 = xExtentDomain[1]; | |
} else { | |
x0 = xExtent[0], x1 = xExtent[1]; | |
if (x.invert) x0 = x.invert(x0), x1 = x.invert(x1); | |
if (x1 < x0) t = x0, x0 = x1, x1 = t; | |
} | |
} | |
if (y) { | |
if (yExtentDomain) { | |
y0 = yExtentDomain[0], y1 = yExtentDomain[1]; | |
} else { | |
y0 = yExtent[0], y1 = yExtent[1]; | |
if (y.invert) y0 = y.invert(y0), y1 = y.invert(y1); | |
if (y1 < y0) t = y0, y0 = y1, y1 = t; | |
} | |
} | |
return x && y ? [ [ x0, y0 ], [ x1, y1 ] ] : x ? [ x0, x1 ] : y && [ y0, y1 ]; | |
} | |
if (x) { | |
x0 = z[0], x1 = z[1]; | |
if (y) x0 = x0[0], x1 = x1[0]; | |
xExtentDomain = [ x0, x1 ]; | |
if (x.invert) x0 = x(x0), x1 = x(x1); | |
if (x1 < x0) t = x0, x0 = x1, x1 = t; | |
if (x0 != xExtent[0] || x1 != xExtent[1]) xExtent = [ x0, x1 ]; | |
} | |
if (y) { | |
y0 = z[0], y1 = z[1]; | |
if (x) y0 = y0[1], y1 = y1[1]; | |
yExtentDomain = [ y0, y1 ]; | |
if (y.invert) y0 = y(y0), y1 = y(y1); | |
if (y1 < y0) t = y0, y0 = y1, y1 = t; | |
if (y0 != yExtent[0] || y1 != yExtent[1]) yExtent = [ y0, y1 ]; | |
} | |
return brush; | |
}; | |
brush.clear = function() { | |
if (!brush.empty()) { | |
xExtent = [ 0, 0 ], yExtent = [ 0, 0 ]; | |
xExtentDomain = yExtentDomain = null; | |
} | |
return brush; | |
}; | |
brush.empty = function() { | |
return !!x && xExtent[0] == xExtent[1] || !!y && yExtent[0] == yExtent[1]; | |
}; | |
return d3.rebind(brush, event, "on"); | |
}; | |
var d3_svg_brushCursor = { | |
n: "ns-resize", | |
e: "ew-resize", | |
s: "ns-resize", | |
w: "ew-resize", | |
nw: "nwse-resize", | |
ne: "nesw-resize", | |
se: "nwse-resize", | |
sw: "nesw-resize" | |
}; | |
var d3_svg_brushResizes = [ [ "n", "e", "s", "w", "nw", "ne", "se", "sw" ], [ "e", "w" ], [ "n", "s" ], [] ]; | |
var d3_time_format = d3_time.format = d3_locale_enUS.timeFormat; | |
var d3_time_formatUtc = d3_time_format.utc; | |
var d3_time_formatIso = d3_time_formatUtc("%Y-%m-%dT%H:%M:%S.%LZ"); | |
d3_time_format.iso = Date.prototype.toISOString && +new Date("2000-01-01T00:00:00.000Z") ? d3_time_formatIsoNative : d3_time_formatIso; | |
function d3_time_formatIsoNative(date) { | |
return date.toISOString(); | |
} | |
d3_time_formatIsoNative.parse = function(string) { | |
var date = new Date(string); | |
return isNaN(date) ? null : date; | |
}; | |
d3_time_formatIsoNative.toString = d3_time_formatIso.toString; | |
d3_time.second = d3_time_interval(function(date) { | |
return new d3_date(Math.floor(date / 1e3) * 1e3); | |
}, function(date, offset) { | |
date.setTime(date.getTime() + Math.floor(offset) * 1e3); | |
}, function(date) { | |
return date.getSeconds(); | |
}); | |
d3_time.seconds = d3_time.second.range; | |
d3_time.seconds.utc = d3_time.second.utc.range; | |
d3_time.minute = d3_time_interval(function(date) { | |
return new d3_date(Math.floor(date / 6e4) * 6e4); | |
}, function(date, offset) { | |
date.setTime(date.getTime() + Math.floor(offset) * 6e4); | |
}, function(date) { | |
return date.getMinutes(); | |
}); | |
d3_time.minutes = d3_time.minute.range; | |
d3_time.minutes.utc = d3_time.minute.utc.range; | |
d3_time.hour = d3_time_interval(function(date) { | |
var timezone = date.getTimezoneOffset() / 60; | |
return new d3_date((Math.floor(date / 36e5 - timezone) + timezone) * 36e5); | |
}, function(date, offset) { | |
date.setTime(date.getTime() + Math.floor(offset) * 36e5); | |
}, function(date) { | |
return date.getHours(); | |
}); | |
d3_time.hours = d3_time.hour.range; | |
d3_time.hours.utc = d3_time.hour.utc.range; | |
d3_time.month = d3_time_interval(function(date) { | |
date = d3_time.day(date); | |
date.setDate(1); | |
return date; | |
}, function(date, offset) { | |
date.setMonth(date.getMonth() + offset); | |
}, function(date) { | |
return date.getMonth(); | |
}); | |
d3_time.months = d3_time.month.range; | |
d3_time.months.utc = d3_time.month.utc.range; | |
function d3_time_scale(linear, methods, format) { | |
function scale(x) { | |
return linear(x); | |
} | |
scale.invert = function(x) { | |
return d3_time_scaleDate(linear.invert(x)); | |
}; | |
scale.domain = function(x) { | |
if (!arguments.length) return linear.domain().map(d3_time_scaleDate); | |
linear.domain(x); | |
return scale; | |
}; | |
function tickMethod(extent, count) { | |
var span = extent[1] - extent[0], target = span / count, i = d3.bisect(d3_time_scaleSteps, target); | |
return i == d3_time_scaleSteps.length ? [ methods.year, d3_scale_linearTickRange(extent.map(function(d) { | |
return d / 31536e6; | |
}), count)[2] ] : !i ? [ d3_time_scaleMilliseconds, d3_scale_linearTickRange(extent, count)[2] ] : methods[target / d3_time_scaleSteps[i - 1] < d3_time_scaleSteps[i] / target ? i - 1 : i]; | |
} | |
scale.nice = function(interval, skip) { | |
var domain = scale.domain(), extent = d3_scaleExtent(domain), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" && tickMethod(extent, interval); | |
if (method) interval = method[0], skip = method[1]; | |
function skipped(date) { | |
return !isNaN(date) && !interval.range(date, d3_time_scaleDate(+date + 1), skip).length; | |
} | |
return scale.domain(d3_scale_nice(domain, skip > 1 ? { | |
floor: function(date) { | |
while (skipped(date = interval.floor(date))) date = d3_time_scaleDate(date - 1); | |
return date; | |
}, | |
ceil: function(date) { | |
while (skipped(date = interval.ceil(date))) date = d3_time_scaleDate(+date + 1); | |
return date; | |
} | |
} : interval)); | |
}; | |
scale.ticks = function(interval, skip) { | |
var extent = d3_scaleExtent(scale.domain()), method = interval == null ? tickMethod(extent, 10) : typeof interval === "number" ? tickMethod(extent, interval) : !interval.range && [ { | |
range: interval | |
}, skip ]; | |
if (method) interval = method[0], skip = method[1]; | |
return interval.range(extent[0], d3_time_scaleDate(+extent[1] + 1), skip < 1 ? 1 : skip); | |
}; | |
scale.tickFormat = function() { | |
return format; | |
}; | |
scale.copy = function() { | |
return d3_time_scale(linear.copy(), methods, format); | |
}; | |
return d3_scale_linearRebind(scale, linear); | |
} | |
function d3_time_scaleDate(t) { | |
return new Date(t); | |
} | |
var d3_time_scaleSteps = [ 1e3, 5e3, 15e3, 3e4, 6e4, 3e5, 9e5, 18e5, 36e5, 108e5, 216e5, 432e5, 864e5, 1728e5, 6048e5, 2592e6, 7776e6, 31536e6 ]; | |
var d3_time_scaleLocalMethods = [ [ d3_time.second, 1 ], [ d3_time.second, 5 ], [ d3_time.second, 15 ], [ d3_time.second, 30 ], [ d3_time.minute, 1 ], [ d3_time.minute, 5 ], [ d3_time.minute, 15 ], [ d3_time.minute, 30 ], [ d3_time.hour, 1 ], [ d3_time.hour, 3 ], [ d3_time.hour, 6 ], [ d3_time.hour, 12 ], [ d3_time.day, 1 ], [ d3_time.day, 2 ], [ d3_time.week, 1 ], [ d3_time.month, 1 ], [ d3_time.month, 3 ], [ d3_time.year, 1 ] ]; | |
var d3_time_scaleLocalFormat = d3_time_format.multi([ [ ".%L", function(d) { | |
return d.getMilliseconds(); | |
} ], [ ":%S", function(d) { | |
return d.getSeconds(); | |
} ], [ "%I:%M", function(d) { | |
return d.getMinutes(); | |
} ], [ "%I %p", function(d) { | |
return d.getHours(); | |
} ], [ "%a %d", function(d) { | |
return d.getDay() && d.getDate() != 1; | |
} ], [ "%b %d", function(d) { | |
return d.getDate() != 1; | |
} ], [ "%B", function(d) { | |
return d.getMonth(); | |
} ], [ "%Y", d3_true ] ]); | |
var d3_time_scaleMilliseconds = { | |
range: function(start, stop, step) { | |
return d3.range(Math.ceil(start / step) * step, +stop, step).map(d3_time_scaleDate); | |
}, | |
floor: d3_identity, | |
ceil: d3_identity | |
}; | |
d3_time_scaleLocalMethods.year = d3_time.year; | |
d3_time.scale = function() { | |
return d3_time_scale(d3.scale.linear(), d3_time_scaleLocalMethods, d3_time_scaleLocalFormat); | |
}; | |
var d3_time_scaleUtcMethods = d3_time_scaleLocalMethods.map(function(m) { | |
return [ m[0].utc, m[1] ]; | |
}); | |
var d3_time_scaleUtcFormat = d3_time_formatUtc.multi([ [ ".%L", function(d) { | |
return d.getUTCMilliseconds(); | |
} ], [ ":%S", function(d) { | |
return d.getUTCSeconds(); | |
} ], [ "%I:%M", function(d) { | |
return d.getUTCMinutes(); | |
} ], [ "%I %p", function(d) { | |
return d.getUTCHours(); | |
} ], [ "%a %d", function(d) { | |
return d.getUTCDay() && d.getUTCDate() != 1; | |
} ], [ "%b %d", function(d) { | |
return d.getUTCDate() != 1; | |
} ], [ "%B", function(d) { | |
return d.getUTCMonth(); | |
} ], [ "%Y", d3_true ] ]); | |
d3_time_scaleUtcMethods.year = d3_time.year.utc; | |
d3_time.scale.utc = function() { | |
return d3_time_scale(d3.scale.linear(), d3_time_scaleUtcMethods, d3_time_scaleUtcFormat); | |
}; | |
d3.text = d3_xhrType(function(request) { | |
return request.responseText; | |
}); | |
d3.json = function(url, callback) { | |
return d3_xhr(url, "application/json", d3_json, callback); | |
}; | |
function d3_json(request) { | |
return JSON.parse(request.responseText); | |
} | |
d3.html = function(url, callback) { | |
return d3_xhr(url, "text/html", d3_html, callback); | |
}; | |
function d3_html(request) { | |
var range = d3_document.createRange(); | |
range.selectNode(d3_document.body); | |
return range.createContextualFragment(request.responseText); | |
} | |
d3.xml = d3_xhrType(function(request) { | |
return request.responseXML; | |
}); | |
if (typeof define === "function" && define.amd) define(d3); else if (typeof module === "object" && module.exports) module.exports = d3; | |
this.d3 = d3; | |
}(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<meta charset="utf-8"> | |
<script id="fragmentShader" type="x-shader/x-fragment"> | |
uniform vec2 u_resolution; | |
uniform float u_time; | |
void main() { | |
vec2 st = gl_FragCoord.xy/u_resolution.xy; | |
gl_FragColor=vec4(st.x,st.y,0.0,1.0); | |
} | |
</script> | |
<body> | |
<style>body{margin: 0px;}</style> | |
<div id="container"></div> | |
<script src="three.js"></script> | |
<script src="d3.js"></script> | |
<script id="vertexShader" type="x-shader/x-vertex"> | |
void main() { | |
gl_Position = vec4( position, 1.0 ); | |
} | |
</script> | |
<script> | |
var container; | |
var camera, scene, renderer; | |
var uniforms; | |
d3.xhr('12.glsl', function(res){ | |
fragShader = res.response | |
init() | |
animate() | |
}) | |
function init() { | |
container = document.getElementById( 'container' ); | |
camera = new THREE.Camera(); | |
camera.position.z = 1; | |
scene = new THREE.Scene(); | |
var geometry = new THREE.PlaneBufferGeometry(2, 2); | |
uniforms = { | |
u_time: { type: "f", value: 1.0 }, | |
u_resolution: { type: "v2", value: new THREE.Vector2() }, | |
u_mouse: { type: "v2", value: new THREE.Vector2() } | |
}; | |
var material = new THREE.ShaderMaterial( { | |
uniforms: uniforms, | |
vertexShader: document.getElementById( 'vertexShader' ).textContent, | |
fragmentShader: fragShader | |
} ); | |
var mesh = new THREE.Mesh( geometry, material ); | |
scene.add( mesh ); | |
renderer = new THREE.WebGLRenderer(); | |
renderer.setPixelRatio( window.devicePixelRatio ); | |
container.appendChild( renderer.domElement ); | |
onWindowResize(); | |
window.addEventListener( 'resize', onWindowResize, false ); | |
document.onmousemove = function(e){ | |
uniforms.u_mouse.value.x = e.pageX | |
uniforms.u_mouse.value.y = e.pageY | |
} | |
} | |
function onWindowResize( event ) { | |
renderer.setSize( window.innerWidth, window.innerHeight ); | |
uniforms.u_resolution.value.x = renderer.domElement.width; | |
uniforms.u_resolution.value.y = renderer.domElement.height; | |
} | |
function animate() { | |
requestAnimationFrame( animate ); | |
render(); | |
} | |
function render() { | |
uniforms.u_time.value += 0.05; | |
renderer.render( scene, camera ); | |
} | |
</script> | |
</body> |
This file has been truncated, but you can view the full file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// File:src/Three.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
var THREE = { REVISION: '74' }; | |
// | |
if ( typeof define === 'function' && define.amd ) { | |
define( 'three', THREE ); | |
} else if ( 'undefined' !== typeof exports && 'undefined' !== typeof module ) { | |
module.exports = THREE; | |
} | |
// | |
if ( Number.EPSILON === undefined ) { | |
Number.EPSILON = Math.pow( 2, - 52 ); | |
} | |
// | |
if ( Math.sign === undefined ) { | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign | |
Math.sign = function ( x ) { | |
return ( x < 0 ) ? - 1 : ( x > 0 ) ? 1 : + x; | |
}; | |
} | |
if ( Function.prototype.name === undefined && Object.defineProperty !== undefined ) { | |
// Missing in IE9-11. | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name | |
Object.defineProperty( Function.prototype, 'name', { | |
get: function () { | |
return this.toString().match( /^\s*function\s*(\S*)\s*\(/ )[ 1 ]; | |
} | |
} ); | |
} | |
if ( Object.assign === undefined ) { | |
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign | |
Object.defineProperty( Object, 'assign', { | |
writable: true, | |
configurable: true, | |
value: function ( target ) { | |
'use strict'; | |
if ( target === undefined || target === null ) { | |
throw new TypeError( "Cannot convert first argument to object" ); | |
} | |
var to = Object( target ); | |
for ( var i = 1, n = arguments.length; i !== n; ++ i ) { | |
var nextSource = arguments[ i ]; | |
if ( nextSource === undefined || nextSource === null ) continue; | |
nextSource = Object( nextSource ); | |
var keysArray = Object.keys( nextSource ); | |
for ( var nextIndex = 0, len = keysArray.length; nextIndex !== len; ++ nextIndex ) { | |
var nextKey = keysArray[ nextIndex ]; | |
var desc = Object.getOwnPropertyDescriptor( nextSource, nextKey ); | |
if ( desc !== undefined && desc.enumerable ) { | |
to[ nextKey ] = nextSource[ nextKey ]; | |
} | |
} | |
} | |
return to; | |
} | |
} ); | |
} | |
// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent.button | |
THREE.MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2 }; | |
// GL STATE CONSTANTS | |
THREE.CullFaceNone = 0; | |
THREE.CullFaceBack = 1; | |
THREE.CullFaceFront = 2; | |
THREE.CullFaceFrontBack = 3; | |
THREE.FrontFaceDirectionCW = 0; | |
THREE.FrontFaceDirectionCCW = 1; | |
// SHADOWING TYPES | |
THREE.BasicShadowMap = 0; | |
THREE.PCFShadowMap = 1; | |
THREE.PCFSoftShadowMap = 2; | |
// MATERIAL CONSTANTS | |
// side | |
THREE.FrontSide = 0; | |
THREE.BackSide = 1; | |
THREE.DoubleSide = 2; | |
// shading | |
THREE.FlatShading = 1; | |
THREE.SmoothShading = 2; | |
// colors | |
THREE.NoColors = 0; | |
THREE.FaceColors = 1; | |
THREE.VertexColors = 2; | |
// blending modes | |
THREE.NoBlending = 0; | |
THREE.NormalBlending = 1; | |
THREE.AdditiveBlending = 2; | |
THREE.SubtractiveBlending = 3; | |
THREE.MultiplyBlending = 4; | |
THREE.CustomBlending = 5; | |
// custom blending equations | |
// (numbers start from 100 not to clash with other | |
// mappings to OpenGL constants defined in Texture.js) | |
THREE.AddEquation = 100; | |
THREE.SubtractEquation = 101; | |
THREE.ReverseSubtractEquation = 102; | |
THREE.MinEquation = 103; | |
THREE.MaxEquation = 104; | |
// custom blending destination factors | |
THREE.ZeroFactor = 200; | |
THREE.OneFactor = 201; | |
THREE.SrcColorFactor = 202; | |
THREE.OneMinusSrcColorFactor = 203; | |
THREE.SrcAlphaFactor = 204; | |
THREE.OneMinusSrcAlphaFactor = 205; | |
THREE.DstAlphaFactor = 206; | |
THREE.OneMinusDstAlphaFactor = 207; | |
// custom blending source factors | |
//THREE.ZeroFactor = 200; | |
//THREE.OneFactor = 201; | |
//THREE.SrcAlphaFactor = 204; | |
//THREE.OneMinusSrcAlphaFactor = 205; | |
//THREE.DstAlphaFactor = 206; | |
//THREE.OneMinusDstAlphaFactor = 207; | |
THREE.DstColorFactor = 208; | |
THREE.OneMinusDstColorFactor = 209; | |
THREE.SrcAlphaSaturateFactor = 210; | |
// depth modes | |
THREE.NeverDepth = 0; | |
THREE.AlwaysDepth = 1; | |
THREE.LessDepth = 2; | |
THREE.LessEqualDepth = 3; | |
THREE.EqualDepth = 4; | |
THREE.GreaterEqualDepth = 5; | |
THREE.GreaterDepth = 6; | |
THREE.NotEqualDepth = 7; | |
// TEXTURE CONSTANTS | |
THREE.MultiplyOperation = 0; | |
THREE.MixOperation = 1; | |
THREE.AddOperation = 2; | |
// Mapping modes | |
THREE.UVMapping = 300; | |
THREE.CubeReflectionMapping = 301; | |
THREE.CubeRefractionMapping = 302; | |
THREE.EquirectangularReflectionMapping = 303; | |
THREE.EquirectangularRefractionMapping = 304; | |
THREE.SphericalReflectionMapping = 305; | |
// Wrapping modes | |
THREE.RepeatWrapping = 1000; | |
THREE.ClampToEdgeWrapping = 1001; | |
THREE.MirroredRepeatWrapping = 1002; | |
// Filters | |
THREE.NearestFilter = 1003; | |
THREE.NearestMipMapNearestFilter = 1004; | |
THREE.NearestMipMapLinearFilter = 1005; | |
THREE.LinearFilter = 1006; | |
THREE.LinearMipMapNearestFilter = 1007; | |
THREE.LinearMipMapLinearFilter = 1008; | |
// Data types | |
THREE.UnsignedByteType = 1009; | |
THREE.ByteType = 1010; | |
THREE.ShortType = 1011; | |
THREE.UnsignedShortType = 1012; | |
THREE.IntType = 1013; | |
THREE.UnsignedIntType = 1014; | |
THREE.FloatType = 1015; | |
THREE.HalfFloatType = 1025; | |
// Pixel types | |
//THREE.UnsignedByteType = 1009; | |
THREE.UnsignedShort4444Type = 1016; | |
THREE.UnsignedShort5551Type = 1017; | |
THREE.UnsignedShort565Type = 1018; | |
// Pixel formats | |
THREE.AlphaFormat = 1019; | |
THREE.RGBFormat = 1020; | |
THREE.RGBAFormat = 1021; | |
THREE.LuminanceFormat = 1022; | |
THREE.LuminanceAlphaFormat = 1023; | |
// THREE.RGBEFormat handled as THREE.RGBAFormat in shaders | |
THREE.RGBEFormat = THREE.RGBAFormat; //1024; | |
// DDS / ST3C Compressed texture formats | |
THREE.RGB_S3TC_DXT1_Format = 2001; | |
THREE.RGBA_S3TC_DXT1_Format = 2002; | |
THREE.RGBA_S3TC_DXT3_Format = 2003; | |
THREE.RGBA_S3TC_DXT5_Format = 2004; | |
// PVRTC compressed texture formats | |
THREE.RGB_PVRTC_4BPPV1_Format = 2100; | |
THREE.RGB_PVRTC_2BPPV1_Format = 2101; | |
THREE.RGBA_PVRTC_4BPPV1_Format = 2102; | |
THREE.RGBA_PVRTC_2BPPV1_Format = 2103; | |
// ETC compressed texture formats | |
THREE.RGB_ETC1_Format = 2151; | |
// Loop styles for AnimationAction | |
THREE.LoopOnce = 2200; | |
THREE.LoopRepeat = 2201; | |
THREE.LoopPingPong = 2202; | |
// Interpolation | |
THREE.InterpolateDiscrete = 2300; | |
THREE.InterpolateLinear = 2301; | |
THREE.InterpolateSmooth = 2302; | |
// Interpolant ending modes | |
THREE.ZeroCurvatureEnding = 2400; | |
THREE.ZeroSlopeEnding = 2401; | |
THREE.WrapAroundEnding = 2402; | |
// Triangle Draw modes | |
THREE.TrianglesDrawMode = 0; | |
THREE.TriangleStripDrawMode = 1; | |
THREE.TriangleFanDrawMode = 2; | |
// File:src/math/Color.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Color = function ( color ) { | |
if ( arguments.length === 3 ) { | |
return this.fromArray( arguments ); | |
} | |
return this.set( color ); | |
}; | |
THREE.Color.prototype = { | |
constructor: THREE.Color, | |
r: 1, g: 1, b: 1, | |
set: function ( value ) { | |
if ( value instanceof THREE.Color ) { | |
this.copy( value ); | |
} else if ( typeof value === 'number' ) { | |
this.setHex( value ); | |
} else if ( typeof value === 'string' ) { | |
this.setStyle( value ); | |
} | |
return this; | |
}, | |
setScalar: function ( scalar ) { | |
this.r = scalar; | |
this.g = scalar; | |
this.b = scalar; | |
}, | |
setHex: function ( hex ) { | |
hex = Math.floor( hex ); | |
this.r = ( hex >> 16 & 255 ) / 255; | |
this.g = ( hex >> 8 & 255 ) / 255; | |
this.b = ( hex & 255 ) / 255; | |
return this; | |
}, | |
setRGB: function ( r, g, b ) { | |
this.r = r; | |
this.g = g; | |
this.b = b; | |
return this; | |
}, | |
setHSL: function () { | |
function hue2rgb( p, q, t ) { | |
if ( t < 0 ) t += 1; | |
if ( t > 1 ) t -= 1; | |
if ( t < 1 / 6 ) return p + ( q - p ) * 6 * t; | |
if ( t < 1 / 2 ) return q; | |
if ( t < 2 / 3 ) return p + ( q - p ) * 6 * ( 2 / 3 - t ); | |
return p; | |
} | |
return function ( h, s, l ) { | |
// h,s,l ranges are in 0.0 - 1.0 | |
h = THREE.Math.euclideanModulo( h, 1 ); | |
s = THREE.Math.clamp( s, 0, 1 ); | |
l = THREE.Math.clamp( l, 0, 1 ); | |
if ( s === 0 ) { | |
this.r = this.g = this.b = l; | |
} else { | |
var p = l <= 0.5 ? l * ( 1 + s ) : l + s - ( l * s ); | |
var q = ( 2 * l ) - p; | |
this.r = hue2rgb( q, p, h + 1 / 3 ); | |
this.g = hue2rgb( q, p, h ); | |
this.b = hue2rgb( q, p, h - 1 / 3 ); | |
} | |
return this; | |
}; | |
}(), | |
setStyle: function ( style ) { | |
function handleAlpha( string ) { | |
if ( string === undefined ) return; | |
if ( parseFloat( string ) < 1 ) { | |
console.warn( 'THREE.Color: Alpha component of ' + style + ' will be ignored.' ); | |
} | |
} | |
var m; | |
if ( m = /^((?:rgb|hsl)a?)\(\s*([^\)]*)\)/.exec( style ) ) { | |
// rgb / hsl | |
var color; | |
var name = m[ 1 ]; | |
var components = m[ 2 ]; | |
switch ( name ) { | |
case 'rgb': | |
case 'rgba': | |
if ( color = /^(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
// rgb(255,0,0) rgba(255,0,0,0.5) | |
this.r = Math.min( 255, parseInt( color[ 1 ], 10 ) ) / 255; | |
this.g = Math.min( 255, parseInt( color[ 2 ], 10 ) ) / 255; | |
this.b = Math.min( 255, parseInt( color[ 3 ], 10 ) ) / 255; | |
handleAlpha( color[ 5 ] ); | |
return this; | |
} | |
if ( color = /^(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
// rgb(100%,0%,0%) rgba(100%,0%,0%,0.5) | |
this.r = Math.min( 100, parseInt( color[ 1 ], 10 ) ) / 100; | |
this.g = Math.min( 100, parseInt( color[ 2 ], 10 ) ) / 100; | |
this.b = Math.min( 100, parseInt( color[ 3 ], 10 ) ) / 100; | |
handleAlpha( color[ 5 ] ); | |
return this; | |
} | |
break; | |
case 'hsl': | |
case 'hsla': | |
if ( color = /^([0-9]*\.?[0-9]+)\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(,\s*([0-9]*\.?[0-9]+)\s*)?$/.exec( components ) ) { | |
// hsl(120,50%,50%) hsla(120,50%,50%,0.5) | |
var h = parseFloat( color[ 1 ] ) / 360; | |
var s = parseInt( color[ 2 ], 10 ) / 100; | |
var l = parseInt( color[ 3 ], 10 ) / 100; | |
handleAlpha( color[ 5 ] ); | |
return this.setHSL( h, s, l ); | |
} | |
break; | |
} | |
} else if ( m = /^\#([A-Fa-f0-9]+)$/.exec( style ) ) { | |
// hex color | |
var hex = m[ 1 ]; | |
var size = hex.length; | |
if ( size === 3 ) { | |
// #ff0 | |
this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 0 ), 16 ) / 255; | |
this.g = parseInt( hex.charAt( 1 ) + hex.charAt( 1 ), 16 ) / 255; | |
this.b = parseInt( hex.charAt( 2 ) + hex.charAt( 2 ), 16 ) / 255; | |
return this; | |
} else if ( size === 6 ) { | |
// #ff0000 | |
this.r = parseInt( hex.charAt( 0 ) + hex.charAt( 1 ), 16 ) / 255; | |
this.g = parseInt( hex.charAt( 2 ) + hex.charAt( 3 ), 16 ) / 255; | |
this.b = parseInt( hex.charAt( 4 ) + hex.charAt( 5 ), 16 ) / 255; | |
return this; | |
} | |
} | |
if ( style && style.length > 0 ) { | |
// color keywords | |
var hex = THREE.ColorKeywords[ style ]; | |
if ( hex !== undefined ) { | |
// red | |
this.setHex( hex ); | |
} else { | |
// unknown color | |
console.warn( 'THREE.Color: Unknown color ' + style ); | |
} | |
} | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor( this.r, this.g, this.b ); | |
}, | |
copy: function ( color ) { | |
this.r = color.r; | |
this.g = color.g; | |
this.b = color.b; | |
return this; | |
}, | |
copyGammaToLinear: function ( color, gammaFactor ) { | |
if ( gammaFactor === undefined ) gammaFactor = 2.0; | |
this.r = Math.pow( color.r, gammaFactor ); | |
this.g = Math.pow( color.g, gammaFactor ); | |
this.b = Math.pow( color.b, gammaFactor ); | |
return this; | |
}, | |
copyLinearToGamma: function ( color, gammaFactor ) { | |
if ( gammaFactor === undefined ) gammaFactor = 2.0; | |
var safeInverse = ( gammaFactor > 0 ) ? ( 1.0 / gammaFactor ) : 1.0; | |
this.r = Math.pow( color.r, safeInverse ); | |
this.g = Math.pow( color.g, safeInverse ); | |
this.b = Math.pow( color.b, safeInverse ); | |
return this; | |
}, | |
convertGammaToLinear: function () { | |
var r = this.r, g = this.g, b = this.b; | |
this.r = r * r; | |
this.g = g * g; | |
this.b = b * b; | |
return this; | |
}, | |
convertLinearToGamma: function () { | |
this.r = Math.sqrt( this.r ); | |
this.g = Math.sqrt( this.g ); | |
this.b = Math.sqrt( this.b ); | |
return this; | |
}, | |
getHex: function () { | |
return ( this.r * 255 ) << 16 ^ ( this.g * 255 ) << 8 ^ ( this.b * 255 ) << 0; | |
}, | |
getHexString: function () { | |
return ( '000000' + this.getHex().toString( 16 ) ).slice( - 6 ); | |
}, | |
getHSL: function ( optionalTarget ) { | |
// h,s,l ranges are in 0.0 - 1.0 | |
var hsl = optionalTarget || { h: 0, s: 0, l: 0 }; | |
var r = this.r, g = this.g, b = this.b; | |
var max = Math.max( r, g, b ); | |
var min = Math.min( r, g, b ); | |
var hue, saturation; | |
var lightness = ( min + max ) / 2.0; | |
if ( min === max ) { | |
hue = 0; | |
saturation = 0; | |
} else { | |
var delta = max - min; | |
saturation = lightness <= 0.5 ? delta / ( max + min ) : delta / ( 2 - max - min ); | |
switch ( max ) { | |
case r: hue = ( g - b ) / delta + ( g < b ? 6 : 0 ); break; | |
case g: hue = ( b - r ) / delta + 2; break; | |
case b: hue = ( r - g ) / delta + 4; break; | |
} | |
hue /= 6; | |
} | |
hsl.h = hue; | |
hsl.s = saturation; | |
hsl.l = lightness; | |
return hsl; | |
}, | |
getStyle: function () { | |
return 'rgb(' + ( ( this.r * 255 ) | 0 ) + ',' + ( ( this.g * 255 ) | 0 ) + ',' + ( ( this.b * 255 ) | 0 ) + ')'; | |
}, | |
offsetHSL: function ( h, s, l ) { | |
var hsl = this.getHSL(); | |
hsl.h += h; hsl.s += s; hsl.l += l; | |
this.setHSL( hsl.h, hsl.s, hsl.l ); | |
return this; | |
}, | |
add: function ( color ) { | |
this.r += color.r; | |
this.g += color.g; | |
this.b += color.b; | |
return this; | |
}, | |
addColors: function ( color1, color2 ) { | |
this.r = color1.r + color2.r; | |
this.g = color1.g + color2.g; | |
this.b = color1.b + color2.b; | |
return this; | |
}, | |
addScalar: function ( s ) { | |
this.r += s; | |
this.g += s; | |
this.b += s; | |
return this; | |
}, | |
multiply: function ( color ) { | |
this.r *= color.r; | |
this.g *= color.g; | |
this.b *= color.b; | |
return this; | |
}, | |
multiplyScalar: function ( s ) { | |
this.r *= s; | |
this.g *= s; | |
this.b *= s; | |
return this; | |
}, | |
lerp: function ( color, alpha ) { | |
this.r += ( color.r - this.r ) * alpha; | |
this.g += ( color.g - this.g ) * alpha; | |
this.b += ( color.b - this.b ) * alpha; | |
return this; | |
}, | |
equals: function ( c ) { | |
return ( c.r === this.r ) && ( c.g === this.g ) && ( c.b === this.b ); | |
}, | |
fromArray: function ( array, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.r = array[ offset ]; | |
this.g = array[ offset + 1 ]; | |
this.b = array[ offset + 2 ]; | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this.r; | |
array[ offset + 1 ] = this.g; | |
array[ offset + 2 ] = this.b; | |
return array; | |
} | |
}; | |
THREE.ColorKeywords = { 'aliceblue': 0xF0F8FF, 'antiquewhite': 0xFAEBD7, 'aqua': 0x00FFFF, 'aquamarine': 0x7FFFD4, 'azure': 0xF0FFFF, | |
'beige': 0xF5F5DC, 'bisque': 0xFFE4C4, 'black': 0x000000, 'blanchedalmond': 0xFFEBCD, 'blue': 0x0000FF, 'blueviolet': 0x8A2BE2, | |
'brown': 0xA52A2A, 'burlywood': 0xDEB887, 'cadetblue': 0x5F9EA0, 'chartreuse': 0x7FFF00, 'chocolate': 0xD2691E, 'coral': 0xFF7F50, | |
'cornflowerblue': 0x6495ED, 'cornsilk': 0xFFF8DC, 'crimson': 0xDC143C, 'cyan': 0x00FFFF, 'darkblue': 0x00008B, 'darkcyan': 0x008B8B, | |
'darkgoldenrod': 0xB8860B, 'darkgray': 0xA9A9A9, 'darkgreen': 0x006400, 'darkgrey': 0xA9A9A9, 'darkkhaki': 0xBDB76B, 'darkmagenta': 0x8B008B, | |
'darkolivegreen': 0x556B2F, 'darkorange': 0xFF8C00, 'darkorchid': 0x9932CC, 'darkred': 0x8B0000, 'darksalmon': 0xE9967A, 'darkseagreen': 0x8FBC8F, | |
'darkslateblue': 0x483D8B, 'darkslategray': 0x2F4F4F, 'darkslategrey': 0x2F4F4F, 'darkturquoise': 0x00CED1, 'darkviolet': 0x9400D3, | |
'deeppink': 0xFF1493, 'deepskyblue': 0x00BFFF, 'dimgray': 0x696969, 'dimgrey': 0x696969, 'dodgerblue': 0x1E90FF, 'firebrick': 0xB22222, | |
'floralwhite': 0xFFFAF0, 'forestgreen': 0x228B22, 'fuchsia': 0xFF00FF, 'gainsboro': 0xDCDCDC, 'ghostwhite': 0xF8F8FF, 'gold': 0xFFD700, | |
'goldenrod': 0xDAA520, 'gray': 0x808080, 'green': 0x008000, 'greenyellow': 0xADFF2F, 'grey': 0x808080, 'honeydew': 0xF0FFF0, 'hotpink': 0xFF69B4, | |
'indianred': 0xCD5C5C, 'indigo': 0x4B0082, 'ivory': 0xFFFFF0, 'khaki': 0xF0E68C, 'lavender': 0xE6E6FA, 'lavenderblush': 0xFFF0F5, 'lawngreen': 0x7CFC00, | |
'lemonchiffon': 0xFFFACD, 'lightblue': 0xADD8E6, 'lightcoral': 0xF08080, 'lightcyan': 0xE0FFFF, 'lightgoldenrodyellow': 0xFAFAD2, 'lightgray': 0xD3D3D3, | |
'lightgreen': 0x90EE90, 'lightgrey': 0xD3D3D3, 'lightpink': 0xFFB6C1, 'lightsalmon': 0xFFA07A, 'lightseagreen': 0x20B2AA, 'lightskyblue': 0x87CEFA, | |
'lightslategray': 0x778899, 'lightslategrey': 0x778899, 'lightsteelblue': 0xB0C4DE, 'lightyellow': 0xFFFFE0, 'lime': 0x00FF00, 'limegreen': 0x32CD32, | |
'linen': 0xFAF0E6, 'magenta': 0xFF00FF, 'maroon': 0x800000, 'mediumaquamarine': 0x66CDAA, 'mediumblue': 0x0000CD, 'mediumorchid': 0xBA55D3, | |
'mediumpurple': 0x9370DB, 'mediumseagreen': 0x3CB371, 'mediumslateblue': 0x7B68EE, 'mediumspringgreen': 0x00FA9A, 'mediumturquoise': 0x48D1CC, | |
'mediumvioletred': 0xC71585, 'midnightblue': 0x191970, 'mintcream': 0xF5FFFA, 'mistyrose': 0xFFE4E1, 'moccasin': 0xFFE4B5, 'navajowhite': 0xFFDEAD, | |
'navy': 0x000080, 'oldlace': 0xFDF5E6, 'olive': 0x808000, 'olivedrab': 0x6B8E23, 'orange': 0xFFA500, 'orangered': 0xFF4500, 'orchid': 0xDA70D6, | |
'palegoldenrod': 0xEEE8AA, 'palegreen': 0x98FB98, 'paleturquoise': 0xAFEEEE, 'palevioletred': 0xDB7093, 'papayawhip': 0xFFEFD5, 'peachpuff': 0xFFDAB9, | |
'peru': 0xCD853F, 'pink': 0xFFC0CB, 'plum': 0xDDA0DD, 'powderblue': 0xB0E0E6, 'purple': 0x800080, 'red': 0xFF0000, 'rosybrown': 0xBC8F8F, | |
'royalblue': 0x4169E1, 'saddlebrown': 0x8B4513, 'salmon': 0xFA8072, 'sandybrown': 0xF4A460, 'seagreen': 0x2E8B57, 'seashell': 0xFFF5EE, | |
'sienna': 0xA0522D, 'silver': 0xC0C0C0, 'skyblue': 0x87CEEB, 'slateblue': 0x6A5ACD, 'slategray': 0x708090, 'slategrey': 0x708090, 'snow': 0xFFFAFA, | |
'springgreen': 0x00FF7F, 'steelblue': 0x4682B4, 'tan': 0xD2B48C, 'teal': 0x008080, 'thistle': 0xD8BFD8, 'tomato': 0xFF6347, 'turquoise': 0x40E0D0, | |
'violet': 0xEE82EE, 'wheat': 0xF5DEB3, 'white': 0xFFFFFF, 'whitesmoke': 0xF5F5F5, 'yellow': 0xFFFF00, 'yellowgreen': 0x9ACD32 }; | |
// File:src/math/Quaternion.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Quaternion = function ( x, y, z, w ) { | |
this._x = x || 0; | |
this._y = y || 0; | |
this._z = z || 0; | |
this._w = ( w !== undefined ) ? w : 1; | |
}; | |
THREE.Quaternion.prototype = { | |
constructor: THREE.Quaternion, | |
get x () { | |
return this._x; | |
}, | |
set x ( value ) { | |
this._x = value; | |
this.onChangeCallback(); | |
}, | |
get y () { | |
return this._y; | |
}, | |
set y ( value ) { | |
this._y = value; | |
this.onChangeCallback(); | |
}, | |
get z () { | |
return this._z; | |
}, | |
set z ( value ) { | |
this._z = value; | |
this.onChangeCallback(); | |
}, | |
get w () { | |
return this._w; | |
}, | |
set w ( value ) { | |
this._w = value; | |
this.onChangeCallback(); | |
}, | |
set: function ( x, y, z, w ) { | |
this._x = x; | |
this._y = y; | |
this._z = z; | |
this._w = w; | |
this.onChangeCallback(); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor( this._x, this._y, this._z, this._w ); | |
}, | |
copy: function ( quaternion ) { | |
this._x = quaternion.x; | |
this._y = quaternion.y; | |
this._z = quaternion.z; | |
this._w = quaternion.w; | |
this.onChangeCallback(); | |
return this; | |
}, | |
setFromEuler: function ( euler, update ) { | |
if ( euler instanceof THREE.Euler === false ) { | |
throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); | |
} | |
// http://www.mathworks.com/matlabcentral/fileexchange/ | |
// 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/ | |
// content/SpinCalc.m | |
var c1 = Math.cos( euler._x / 2 ); | |
var c2 = Math.cos( euler._y / 2 ); | |
var c3 = Math.cos( euler._z / 2 ); | |
var s1 = Math.sin( euler._x / 2 ); | |
var s2 = Math.sin( euler._y / 2 ); | |
var s3 = Math.sin( euler._z / 2 ); | |
var order = euler.order; | |
if ( order === 'XYZ' ) { | |
this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
} else if ( order === 'YXZ' ) { | |
this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
} else if ( order === 'ZXY' ) { | |
this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
} else if ( order === 'ZYX' ) { | |
this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
} else if ( order === 'YZX' ) { | |
this._x = s1 * c2 * c3 + c1 * s2 * s3; | |
this._y = c1 * s2 * c3 + s1 * c2 * s3; | |
this._z = c1 * c2 * s3 - s1 * s2 * c3; | |
this._w = c1 * c2 * c3 - s1 * s2 * s3; | |
} else if ( order === 'XZY' ) { | |
this._x = s1 * c2 * c3 - c1 * s2 * s3; | |
this._y = c1 * s2 * c3 - s1 * c2 * s3; | |
this._z = c1 * c2 * s3 + s1 * s2 * c3; | |
this._w = c1 * c2 * c3 + s1 * s2 * s3; | |
} | |
if ( update !== false ) this.onChangeCallback(); | |
return this; | |
}, | |
setFromAxisAngle: function ( axis, angle ) { | |
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm | |
// assumes axis is normalized | |
var halfAngle = angle / 2, s = Math.sin( halfAngle ); | |
this._x = axis.x * s; | |
this._y = axis.y * s; | |
this._z = axis.z * s; | |
this._w = Math.cos( halfAngle ); | |
this.onChangeCallback(); | |
return this; | |
}, | |
setFromRotationMatrix: function ( m ) { | |
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm | |
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
var te = m.elements, | |
m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], | |
m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], | |
m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ], | |
trace = m11 + m22 + m33, | |
s; | |
if ( trace > 0 ) { | |
s = 0.5 / Math.sqrt( trace + 1.0 ); | |
this._w = 0.25 / s; | |
this._x = ( m32 - m23 ) * s; | |
this._y = ( m13 - m31 ) * s; | |
this._z = ( m21 - m12 ) * s; | |
} else if ( m11 > m22 && m11 > m33 ) { | |
s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 ); | |
this._w = ( m32 - m23 ) / s; | |
this._x = 0.25 * s; | |
this._y = ( m12 + m21 ) / s; | |
this._z = ( m13 + m31 ) / s; | |
} else if ( m22 > m33 ) { | |
s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 ); | |
this._w = ( m13 - m31 ) / s; | |
this._x = ( m12 + m21 ) / s; | |
this._y = 0.25 * s; | |
this._z = ( m23 + m32 ) / s; | |
} else { | |
s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 ); | |
this._w = ( m21 - m12 ) / s; | |
this._x = ( m13 + m31 ) / s; | |
this._y = ( m23 + m32 ) / s; | |
this._z = 0.25 * s; | |
} | |
this.onChangeCallback(); | |
return this; | |
}, | |
setFromUnitVectors: function () { | |
// http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final | |
// assumes direction vectors vFrom and vTo are normalized | |
var v1, r; | |
var EPS = 0.000001; | |
return function ( vFrom, vTo ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
r = vFrom.dot( vTo ) + 1; | |
if ( r < EPS ) { | |
r = 0; | |
if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) { | |
v1.set( - vFrom.y, vFrom.x, 0 ); | |
} else { | |
v1.set( 0, - vFrom.z, vFrom.y ); | |
} | |
} else { | |
v1.crossVectors( vFrom, vTo ); | |
} | |
this._x = v1.x; | |
this._y = v1.y; | |
this._z = v1.z; | |
this._w = r; | |
this.normalize(); | |
return this; | |
}; | |
}(), | |
inverse: function () { | |
this.conjugate().normalize(); | |
return this; | |
}, | |
conjugate: function () { | |
this._x *= - 1; | |
this._y *= - 1; | |
this._z *= - 1; | |
this.onChangeCallback(); | |
return this; | |
}, | |
dot: function ( v ) { | |
return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w; | |
}, | |
lengthSq: function () { | |
return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w; | |
}, | |
length: function () { | |
return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w ); | |
}, | |
normalize: function () { | |
var l = this.length(); | |
if ( l === 0 ) { | |
this._x = 0; | |
this._y = 0; | |
this._z = 0; | |
this._w = 1; | |
} else { | |
l = 1 / l; | |
this._x = this._x * l; | |
this._y = this._y * l; | |
this._z = this._z * l; | |
this._w = this._w * l; | |
} | |
this.onChangeCallback(); | |
return this; | |
}, | |
multiply: function ( q, p ) { | |
if ( p !== undefined ) { | |
console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' ); | |
return this.multiplyQuaternions( q, p ); | |
} | |
return this.multiplyQuaternions( this, q ); | |
}, | |
multiplyQuaternions: function ( a, b ) { | |
// from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm | |
var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w; | |
var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w; | |
this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby; | |
this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz; | |
this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx; | |
this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz; | |
this.onChangeCallback(); | |
return this; | |
}, | |
slerp: function ( qb, t ) { | |
if ( t === 0 ) return this; | |
if ( t === 1 ) return this.copy( qb ); | |
var x = this._x, y = this._y, z = this._z, w = this._w; | |
// http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/ | |
var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z; | |
if ( cosHalfTheta < 0 ) { | |
this._w = - qb._w; | |
this._x = - qb._x; | |
this._y = - qb._y; | |
this._z = - qb._z; | |
cosHalfTheta = - cosHalfTheta; | |
} else { | |
this.copy( qb ); | |
} | |
if ( cosHalfTheta >= 1.0 ) { | |
this._w = w; | |
this._x = x; | |
this._y = y; | |
this._z = z; | |
return this; | |
} | |
var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta ); | |
if ( Math.abs( sinHalfTheta ) < 0.001 ) { | |
this._w = 0.5 * ( w + this._w ); | |
this._x = 0.5 * ( x + this._x ); | |
this._y = 0.5 * ( y + this._y ); | |
this._z = 0.5 * ( z + this._z ); | |
return this; | |
} | |
var halfTheta = Math.atan2( sinHalfTheta, cosHalfTheta ); | |
var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta, | |
ratioB = Math.sin( t * halfTheta ) / sinHalfTheta; | |
this._w = ( w * ratioA + this._w * ratioB ); | |
this._x = ( x * ratioA + this._x * ratioB ); | |
this._y = ( y * ratioA + this._y * ratioB ); | |
this._z = ( z * ratioA + this._z * ratioB ); | |
this.onChangeCallback(); | |
return this; | |
}, | |
equals: function ( quaternion ) { | |
return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w ); | |
}, | |
fromArray: function ( array, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this._x = array[ offset ]; | |
this._y = array[ offset + 1 ]; | |
this._z = array[ offset + 2 ]; | |
this._w = array[ offset + 3 ]; | |
this.onChangeCallback(); | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this._x; | |
array[ offset + 1 ] = this._y; | |
array[ offset + 2 ] = this._z; | |
array[ offset + 3 ] = this._w; | |
return array; | |
}, | |
onChange: function ( callback ) { | |
this.onChangeCallback = callback; | |
return this; | |
}, | |
onChangeCallback: function () {} | |
}; | |
Object.assign( THREE.Quaternion, { | |
slerp: function( qa, qb, qm, t ) { | |
return qm.copy( qa ).slerp( qb, t ); | |
}, | |
slerpFlat: function( | |
dst, dstOffset, src0, srcOffset0, src1, srcOffset1, t ) { | |
// fuzz-free, array-based Quaternion SLERP operation | |
var x0 = src0[ srcOffset0 + 0 ], | |
y0 = src0[ srcOffset0 + 1 ], | |
z0 = src0[ srcOffset0 + 2 ], | |
w0 = src0[ srcOffset0 + 3 ], | |
x1 = src1[ srcOffset1 + 0 ], | |
y1 = src1[ srcOffset1 + 1 ], | |
z1 = src1[ srcOffset1 + 2 ], | |
w1 = src1[ srcOffset1 + 3 ]; | |
if ( w0 !== w1 || x0 !== x1 || y0 !== y1 || z0 !== z1 ) { | |
var s = 1 - t, | |
cos = x0 * x1 + y0 * y1 + z0 * z1 + w0 * w1, | |
dir = ( cos >= 0 ? 1 : - 1 ), | |
sqrSin = 1 - cos * cos; | |
// Skip the Slerp for tiny steps to avoid numeric problems: | |
if ( sqrSin > Number.EPSILON ) { | |
var sin = Math.sqrt( sqrSin ), | |
len = Math.atan2( sin, cos * dir ); | |
s = Math.sin( s * len ) / sin; | |
t = Math.sin( t * len ) / sin; | |
} | |
var tDir = t * dir; | |
x0 = x0 * s + x1 * tDir; | |
y0 = y0 * s + y1 * tDir; | |
z0 = z0 * s + z1 * tDir; | |
w0 = w0 * s + w1 * tDir; | |
// Normalize in case we just did a lerp: | |
if ( s === 1 - t ) { | |
var f = 1 / Math.sqrt( x0 * x0 + y0 * y0 + z0 * z0 + w0 * w0 ); | |
x0 *= f; | |
y0 *= f; | |
z0 *= f; | |
w0 *= f; | |
} | |
} | |
dst[ dstOffset ] = x0; | |
dst[ dstOffset + 1 ] = y0; | |
dst[ dstOffset + 2 ] = z0; | |
dst[ dstOffset + 3 ] = w0; | |
} | |
} ); | |
// File:src/math/Vector2.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author philogb / http://blog.thejit.org/ | |
* @author egraether / http://egraether.com/ | |
* @author zz85 / http://www.lab4games.net/zz85/blog | |
*/ | |
THREE.Vector2 = function ( x, y ) { | |
this.x = x || 0; | |
this.y = y || 0; | |
}; | |
THREE.Vector2.prototype = { | |
constructor: THREE.Vector2, | |
get width() { | |
return this.x; | |
}, | |
set width( value ) { | |
this.x = value; | |
}, | |
get height() { | |
return this.y; | |
}, | |
set height( value ) { | |
this.y = value; | |
}, | |
// | |
set: function ( x, y ) { | |
this.x = x; | |
this.y = y; | |
return this; | |
}, | |
setScalar: function ( scalar ) { | |
this.x = scalar; | |
this.y = scalar; | |
return this; | |
}, | |
setX: function ( x ) { | |
this.x = x; | |
return this; | |
}, | |
setY: function ( y ) { | |
this.y = y; | |
return this; | |
}, | |
setComponent: function ( index, value ) { | |
switch ( index ) { | |
case 0: this.x = value; break; | |
case 1: this.y = value; break; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
getComponent: function ( index ) { | |
switch ( index ) { | |
case 0: return this.x; | |
case 1: return this.y; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
clone: function () { | |
return new this.constructor( this.x, this.y ); | |
}, | |
copy: function ( v ) { | |
this.x = v.x; | |
this.y = v.y; | |
return this; | |
}, | |
add: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
return this.addVectors( v, w ); | |
} | |
this.x += v.x; | |
this.y += v.y; | |
return this; | |
}, | |
addScalar: function ( s ) { | |
this.x += s; | |
this.y += s; | |
return this; | |
}, | |
addVectors: function ( a, b ) { | |
this.x = a.x + b.x; | |
this.y = a.y + b.y; | |
return this; | |
}, | |
addScaledVector: function ( v, s ) { | |
this.x += v.x * s; | |
this.y += v.y * s; | |
return this; | |
}, | |
sub: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
return this.subVectors( v, w ); | |
} | |
this.x -= v.x; | |
this.y -= v.y; | |
return this; | |
}, | |
subScalar: function ( s ) { | |
this.x -= s; | |
this.y -= s; | |
return this; | |
}, | |
subVectors: function ( a, b ) { | |
this.x = a.x - b.x; | |
this.y = a.y - b.y; | |
return this; | |
}, | |
multiply: function ( v ) { | |
this.x *= v.x; | |
this.y *= v.y; | |
return this; | |
}, | |
multiplyScalar: function ( scalar ) { | |
if ( isFinite( scalar ) ) { | |
this.x *= scalar; | |
this.y *= scalar; | |
} else { | |
this.x = 0; | |
this.y = 0; | |
} | |
return this; | |
}, | |
divide: function ( v ) { | |
this.x /= v.x; | |
this.y /= v.y; | |
return this; | |
}, | |
divideScalar: function ( scalar ) { | |
return this.multiplyScalar( 1 / scalar ); | |
}, | |
min: function ( v ) { | |
this.x = Math.min( this.x, v.x ); | |
this.y = Math.min( this.y, v.y ); | |
return this; | |
}, | |
max: function ( v ) { | |
this.x = Math.max( this.x, v.x ); | |
this.y = Math.max( this.y, v.y ); | |
return this; | |
}, | |
clamp: function ( min, max ) { | |
// This function assumes min < max, if this assumption isn't true it will not operate correctly | |
this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
return this; | |
}, | |
clampScalar: function () { | |
var min, max; | |
return function clampScalar( minVal, maxVal ) { | |
if ( min === undefined ) { | |
min = new THREE.Vector2(); | |
max = new THREE.Vector2(); | |
} | |
min.set( minVal, minVal ); | |
max.set( maxVal, maxVal ); | |
return this.clamp( min, max ); | |
}; | |
}(), | |
clampLength: function ( min, max ) { | |
var length = this.length(); | |
this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); | |
return this; | |
}, | |
floor: function () { | |
this.x = Math.floor( this.x ); | |
this.y = Math.floor( this.y ); | |
return this; | |
}, | |
ceil: function () { | |
this.x = Math.ceil( this.x ); | |
this.y = Math.ceil( this.y ); | |
return this; | |
}, | |
round: function () { | |
this.x = Math.round( this.x ); | |
this.y = Math.round( this.y ); | |
return this; | |
}, | |
roundToZero: function () { | |
this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
return this; | |
}, | |
negate: function () { | |
this.x = - this.x; | |
this.y = - this.y; | |
return this; | |
}, | |
dot: function ( v ) { | |
return this.x * v.x + this.y * v.y; | |
}, | |
lengthSq: function () { | |
return this.x * this.x + this.y * this.y; | |
}, | |
length: function () { | |
return Math.sqrt( this.x * this.x + this.y * this.y ); | |
}, | |
lengthManhattan: function() { | |
return Math.abs( this.x ) + Math.abs( this.y ); | |
}, | |
normalize: function () { | |
return this.divideScalar( this.length() ); | |
}, | |
angle: function () { | |
// computes the angle in radians with respect to the positive x-axis | |
var angle = Math.atan2( this.y, this.x ); | |
if ( angle < 0 ) angle += 2 * Math.PI; | |
return angle; | |
}, | |
distanceTo: function ( v ) { | |
return Math.sqrt( this.distanceToSquared( v ) ); | |
}, | |
distanceToSquared: function ( v ) { | |
var dx = this.x - v.x, dy = this.y - v.y; | |
return dx * dx + dy * dy; | |
}, | |
setLength: function ( length ) { | |
return this.multiplyScalar( length / this.length() ); | |
}, | |
lerp: function ( v, alpha ) { | |
this.x += ( v.x - this.x ) * alpha; | |
this.y += ( v.y - this.y ) * alpha; | |
return this; | |
}, | |
lerpVectors: function ( v1, v2, alpha ) { | |
this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
return this; | |
}, | |
equals: function ( v ) { | |
return ( ( v.x === this.x ) && ( v.y === this.y ) ); | |
}, | |
fromArray: function ( array, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.x = array[ offset ]; | |
this.y = array[ offset + 1 ]; | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this.x; | |
array[ offset + 1 ] = this.y; | |
return array; | |
}, | |
fromAttribute: function ( attribute, index, offset ) { | |
if ( offset === undefined ) offset = 0; | |
index = index * attribute.itemSize + offset; | |
this.x = attribute.array[ index ]; | |
this.y = attribute.array[ index + 1 ]; | |
return this; | |
}, | |
rotateAround: function ( center, angle ) { | |
var c = Math.cos( angle ), s = Math.sin( angle ); | |
var x = this.x - center.x; | |
var y = this.y - center.y; | |
this.x = x * c - y * s + center.x; | |
this.y = x * s + y * c + center.y; | |
return this; | |
} | |
}; | |
// File:src/math/Vector3.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author *kile / http://kile.stravaganza.org/ | |
* @author philogb / http://blog.thejit.org/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author egraether / http://egraether.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.Vector3 = function ( x, y, z ) { | |
this.x = x || 0; | |
this.y = y || 0; | |
this.z = z || 0; | |
}; | |
THREE.Vector3.prototype = { | |
constructor: THREE.Vector3, | |
set: function ( x, y, z ) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
return this; | |
}, | |
setScalar: function ( scalar ) { | |
this.x = scalar; | |
this.y = scalar; | |
this.z = scalar; | |
return this; | |
}, | |
setX: function ( x ) { | |
this.x = x; | |
return this; | |
}, | |
setY: function ( y ) { | |
this.y = y; | |
return this; | |
}, | |
setZ: function ( z ) { | |
this.z = z; | |
return this; | |
}, | |
setComponent: function ( index, value ) { | |
switch ( index ) { | |
case 0: this.x = value; break; | |
case 1: this.y = value; break; | |
case 2: this.z = value; break; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
getComponent: function ( index ) { | |
switch ( index ) { | |
case 0: return this.x; | |
case 1: return this.y; | |
case 2: return this.z; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
clone: function () { | |
return new this.constructor( this.x, this.y, this.z ); | |
}, | |
copy: function ( v ) { | |
this.x = v.x; | |
this.y = v.y; | |
this.z = v.z; | |
return this; | |
}, | |
add: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
return this.addVectors( v, w ); | |
} | |
this.x += v.x; | |
this.y += v.y; | |
this.z += v.z; | |
return this; | |
}, | |
addScalar: function ( s ) { | |
this.x += s; | |
this.y += s; | |
this.z += s; | |
return this; | |
}, | |
addVectors: function ( a, b ) { | |
this.x = a.x + b.x; | |
this.y = a.y + b.y; | |
this.z = a.z + b.z; | |
return this; | |
}, | |
addScaledVector: function ( v, s ) { | |
this.x += v.x * s; | |
this.y += v.y * s; | |
this.z += v.z * s; | |
return this; | |
}, | |
sub: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
return this.subVectors( v, w ); | |
} | |
this.x -= v.x; | |
this.y -= v.y; | |
this.z -= v.z; | |
return this; | |
}, | |
subScalar: function ( s ) { | |
this.x -= s; | |
this.y -= s; | |
this.z -= s; | |
return this; | |
}, | |
subVectors: function ( a, b ) { | |
this.x = a.x - b.x; | |
this.y = a.y - b.y; | |
this.z = a.z - b.z; | |
return this; | |
}, | |
multiply: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' ); | |
return this.multiplyVectors( v, w ); | |
} | |
this.x *= v.x; | |
this.y *= v.y; | |
this.z *= v.z; | |
return this; | |
}, | |
multiplyScalar: function ( scalar ) { | |
if ( isFinite( scalar ) ) { | |
this.x *= scalar; | |
this.y *= scalar; | |
this.z *= scalar; | |
} else { | |
this.x = 0; | |
this.y = 0; | |
this.z = 0; | |
} | |
return this; | |
}, | |
multiplyVectors: function ( a, b ) { | |
this.x = a.x * b.x; | |
this.y = a.y * b.y; | |
this.z = a.z * b.z; | |
return this; | |
}, | |
applyEuler: function () { | |
var quaternion; | |
return function applyEuler( euler ) { | |
if ( euler instanceof THREE.Euler === false ) { | |
console.error( 'THREE.Vector3: .applyEuler() now expects an Euler rotation rather than a Vector3 and order.' ); | |
} | |
if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); | |
this.applyQuaternion( quaternion.setFromEuler( euler ) ); | |
return this; | |
}; | |
}(), | |
applyAxisAngle: function () { | |
var quaternion; | |
return function applyAxisAngle( axis, angle ) { | |
if ( quaternion === undefined ) quaternion = new THREE.Quaternion(); | |
this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) ); | |
return this; | |
}; | |
}(), | |
applyMatrix3: function ( m ) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var e = m.elements; | |
this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z; | |
this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z; | |
this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z; | |
return this; | |
}, | |
applyMatrix4: function ( m ) { | |
// input: THREE.Matrix4 affine matrix | |
var x = this.x, y = this.y, z = this.z; | |
var e = m.elements; | |
this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ]; | |
this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ]; | |
this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ]; | |
return this; | |
}, | |
applyProjection: function ( m ) { | |
// input: THREE.Matrix4 projection matrix | |
var x = this.x, y = this.y, z = this.z; | |
var e = m.elements; | |
var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide | |
this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d; | |
this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d; | |
this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d; | |
return this; | |
}, | |
applyQuaternion: function ( q ) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var qx = q.x; | |
var qy = q.y; | |
var qz = q.z; | |
var qw = q.w; | |
// calculate quat * vector | |
var ix = qw * x + qy * z - qz * y; | |
var iy = qw * y + qz * x - qx * z; | |
var iz = qw * z + qx * y - qy * x; | |
var iw = - qx * x - qy * y - qz * z; | |
// calculate result * inverse quat | |
this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy; | |
this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz; | |
this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx; | |
return this; | |
}, | |
project: function () { | |
var matrix; | |
return function project( camera ) { | |
if ( matrix === undefined ) matrix = new THREE.Matrix4(); | |
matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) ); | |
return this.applyProjection( matrix ); | |
}; | |
}(), | |
unproject: function () { | |
var matrix; | |
return function unproject( camera ) { | |
if ( matrix === undefined ) matrix = new THREE.Matrix4(); | |
matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) ); | |
return this.applyProjection( matrix ); | |
}; | |
}(), | |
transformDirection: function ( m ) { | |
// input: THREE.Matrix4 affine matrix | |
// vector interpreted as a direction | |
var x = this.x, y = this.y, z = this.z; | |
var e = m.elements; | |
this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z; | |
this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z; | |
this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z; | |
this.normalize(); | |
return this; | |
}, | |
divide: function ( v ) { | |
this.x /= v.x; | |
this.y /= v.y; | |
this.z /= v.z; | |
return this; | |
}, | |
divideScalar: function ( scalar ) { | |
return this.multiplyScalar( 1 / scalar ); | |
}, | |
min: function ( v ) { | |
this.x = Math.min( this.x, v.x ); | |
this.y = Math.min( this.y, v.y ); | |
this.z = Math.min( this.z, v.z ); | |
return this; | |
}, | |
max: function ( v ) { | |
this.x = Math.max( this.x, v.x ); | |
this.y = Math.max( this.y, v.y ); | |
this.z = Math.max( this.z, v.z ); | |
return this; | |
}, | |
clamp: function ( min, max ) { | |
// This function assumes min < max, if this assumption isn't true it will not operate correctly | |
this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
this.z = Math.max( min.z, Math.min( max.z, this.z ) ); | |
return this; | |
}, | |
clampScalar: function () { | |
var min, max; | |
return function clampScalar( minVal, maxVal ) { | |
if ( min === undefined ) { | |
min = new THREE.Vector3(); | |
max = new THREE.Vector3(); | |
} | |
min.set( minVal, minVal, minVal ); | |
max.set( maxVal, maxVal, maxVal ); | |
return this.clamp( min, max ); | |
}; | |
}(), | |
clampLength: function ( min, max ) { | |
var length = this.length(); | |
this.multiplyScalar( Math.max( min, Math.min( max, length ) ) / length ); | |
return this; | |
}, | |
floor: function () { | |
this.x = Math.floor( this.x ); | |
this.y = Math.floor( this.y ); | |
this.z = Math.floor( this.z ); | |
return this; | |
}, | |
ceil: function () { | |
this.x = Math.ceil( this.x ); | |
this.y = Math.ceil( this.y ); | |
this.z = Math.ceil( this.z ); | |
return this; | |
}, | |
round: function () { | |
this.x = Math.round( this.x ); | |
this.y = Math.round( this.y ); | |
this.z = Math.round( this.z ); | |
return this; | |
}, | |
roundToZero: function () { | |
this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); | |
return this; | |
}, | |
negate: function () { | |
this.x = - this.x; | |
this.y = - this.y; | |
this.z = - this.z; | |
return this; | |
}, | |
dot: function ( v ) { | |
return this.x * v.x + this.y * v.y + this.z * v.z; | |
}, | |
lengthSq: function () { | |
return this.x * this.x + this.y * this.y + this.z * this.z; | |
}, | |
length: function () { | |
return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z ); | |
}, | |
lengthManhattan: function () { | |
return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ); | |
}, | |
normalize: function () { | |
return this.divideScalar( this.length() ); | |
}, | |
setLength: function ( length ) { | |
return this.multiplyScalar( length / this.length() ); | |
}, | |
lerp: function ( v, alpha ) { | |
this.x += ( v.x - this.x ) * alpha; | |
this.y += ( v.y - this.y ) * alpha; | |
this.z += ( v.z - this.z ) * alpha; | |
return this; | |
}, | |
lerpVectors: function ( v1, v2, alpha ) { | |
this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
return this; | |
}, | |
cross: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' ); | |
return this.crossVectors( v, w ); | |
} | |
var x = this.x, y = this.y, z = this.z; | |
this.x = y * v.z - z * v.y; | |
this.y = z * v.x - x * v.z; | |
this.z = x * v.y - y * v.x; | |
return this; | |
}, | |
crossVectors: function ( a, b ) { | |
var ax = a.x, ay = a.y, az = a.z; | |
var bx = b.x, by = b.y, bz = b.z; | |
this.x = ay * bz - az * by; | |
this.y = az * bx - ax * bz; | |
this.z = ax * by - ay * bx; | |
return this; | |
}, | |
projectOnVector: function () { | |
var v1, dot; | |
return function projectOnVector( vector ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
v1.copy( vector ).normalize(); | |
dot = this.dot( v1 ); | |
return this.copy( v1 ).multiplyScalar( dot ); | |
}; | |
}(), | |
projectOnPlane: function () { | |
var v1; | |
return function projectOnPlane( planeNormal ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
v1.copy( this ).projectOnVector( planeNormal ); | |
return this.sub( v1 ); | |
} | |
}(), | |
reflect: function () { | |
// reflect incident vector off plane orthogonal to normal | |
// normal is assumed to have unit length | |
var v1; | |
return function reflect( normal ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) ); | |
} | |
}(), | |
angleTo: function ( v ) { | |
var theta = this.dot( v ) / ( Math.sqrt( this.lengthSq() * v.lengthSq() ) ); | |
// clamp, to handle numerical problems | |
return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) ); | |
}, | |
distanceTo: function ( v ) { | |
return Math.sqrt( this.distanceToSquared( v ) ); | |
}, | |
distanceToSquared: function ( v ) { | |
var dx = this.x - v.x; | |
var dy = this.y - v.y; | |
var dz = this.z - v.z; | |
return dx * dx + dy * dy + dz * dz; | |
}, | |
setFromMatrixPosition: function ( m ) { | |
this.x = m.elements[ 12 ]; | |
this.y = m.elements[ 13 ]; | |
this.z = m.elements[ 14 ]; | |
return this; | |
}, | |
setFromMatrixScale: function ( m ) { | |
var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length(); | |
var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length(); | |
var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length(); | |
this.x = sx; | |
this.y = sy; | |
this.z = sz; | |
return this; | |
}, | |
setFromMatrixColumn: function ( index, matrix ) { | |
var offset = index * 4; | |
var me = matrix.elements; | |
this.x = me[ offset ]; | |
this.y = me[ offset + 1 ]; | |
this.z = me[ offset + 2 ]; | |
return this; | |
}, | |
equals: function ( v ) { | |
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) ); | |
}, | |
fromArray: function ( array, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.x = array[ offset ]; | |
this.y = array[ offset + 1 ]; | |
this.z = array[ offset + 2 ]; | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this.x; | |
array[ offset + 1 ] = this.y; | |
array[ offset + 2 ] = this.z; | |
return array; | |
}, | |
fromAttribute: function ( attribute, index, offset ) { | |
if ( offset === undefined ) offset = 0; | |
index = index * attribute.itemSize + offset; | |
this.x = attribute.array[ index ]; | |
this.y = attribute.array[ index + 1 ]; | |
this.z = attribute.array[ index + 2 ]; | |
return this; | |
} | |
}; | |
// File:src/math/Vector4.js | |
/** | |
* @author supereggbert / http://www.paulbrunt.co.uk/ | |
* @author philogb / http://blog.thejit.org/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author egraether / http://egraether.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.Vector4 = function ( x, y, z, w ) { | |
this.x = x || 0; | |
this.y = y || 0; | |
this.z = z || 0; | |
this.w = ( w !== undefined ) ? w : 1; | |
}; | |
THREE.Vector4.prototype = { | |
constructor: THREE.Vector4, | |
set: function ( x, y, z, w ) { | |
this.x = x; | |
this.y = y; | |
this.z = z; | |
this.w = w; | |
return this; | |
}, | |
setScalar: function ( scalar ) { | |
this.x = scalar; | |
this.y = scalar; | |
this.z = scalar; | |
this.w = scalar; | |
return this; | |
}, | |
setX: function ( x ) { | |
this.x = x; | |
return this; | |
}, | |
setY: function ( y ) { | |
this.y = y; | |
return this; | |
}, | |
setZ: function ( z ) { | |
this.z = z; | |
return this; | |
}, | |
setW: function ( w ) { | |
this.w = w; | |
return this; | |
}, | |
setComponent: function ( index, value ) { | |
switch ( index ) { | |
case 0: this.x = value; break; | |
case 1: this.y = value; break; | |
case 2: this.z = value; break; | |
case 3: this.w = value; break; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
getComponent: function ( index ) { | |
switch ( index ) { | |
case 0: return this.x; | |
case 1: return this.y; | |
case 2: return this.z; | |
case 3: return this.w; | |
default: throw new Error( 'index is out of range: ' + index ); | |
} | |
}, | |
clone: function () { | |
return new this.constructor( this.x, this.y, this.z, this.w ); | |
}, | |
copy: function ( v ) { | |
this.x = v.x; | |
this.y = v.y; | |
this.z = v.z; | |
this.w = ( v.w !== undefined ) ? v.w : 1; | |
return this; | |
}, | |
add: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector4: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' ); | |
return this.addVectors( v, w ); | |
} | |
this.x += v.x; | |
this.y += v.y; | |
this.z += v.z; | |
this.w += v.w; | |
return this; | |
}, | |
addScalar: function ( s ) { | |
this.x += s; | |
this.y += s; | |
this.z += s; | |
this.w += s; | |
return this; | |
}, | |
addVectors: function ( a, b ) { | |
this.x = a.x + b.x; | |
this.y = a.y + b.y; | |
this.z = a.z + b.z; | |
this.w = a.w + b.w; | |
return this; | |
}, | |
addScaledVector: function ( v, s ) { | |
this.x += v.x * s; | |
this.y += v.y * s; | |
this.z += v.z * s; | |
this.w += v.w * s; | |
return this; | |
}, | |
sub: function ( v, w ) { | |
if ( w !== undefined ) { | |
console.warn( 'THREE.Vector4: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' ); | |
return this.subVectors( v, w ); | |
} | |
this.x -= v.x; | |
this.y -= v.y; | |
this.z -= v.z; | |
this.w -= v.w; | |
return this; | |
}, | |
subScalar: function ( s ) { | |
this.x -= s; | |
this.y -= s; | |
this.z -= s; | |
this.w -= s; | |
return this; | |
}, | |
subVectors: function ( a, b ) { | |
this.x = a.x - b.x; | |
this.y = a.y - b.y; | |
this.z = a.z - b.z; | |
this.w = a.w - b.w; | |
return this; | |
}, | |
multiplyScalar: function ( scalar ) { | |
if ( isFinite( scalar ) ) { | |
this.x *= scalar; | |
this.y *= scalar; | |
this.z *= scalar; | |
this.w *= scalar; | |
} else { | |
this.x = 0; | |
this.y = 0; | |
this.z = 0; | |
this.w = 0; | |
} | |
return this; | |
}, | |
applyMatrix4: function ( m ) { | |
var x = this.x; | |
var y = this.y; | |
var z = this.z; | |
var w = this.w; | |
var e = m.elements; | |
this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] * w; | |
this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] * w; | |
this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] * w; | |
this.w = e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] * w; | |
return this; | |
}, | |
divideScalar: function ( scalar ) { | |
return this.multiplyScalar( 1 / scalar ); | |
}, | |
setAxisAngleFromQuaternion: function ( q ) { | |
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm | |
// q is assumed to be normalized | |
this.w = 2 * Math.acos( q.w ); | |
var s = Math.sqrt( 1 - q.w * q.w ); | |
if ( s < 0.0001 ) { | |
this.x = 1; | |
this.y = 0; | |
this.z = 0; | |
} else { | |
this.x = q.x / s; | |
this.y = q.y / s; | |
this.z = q.z / s; | |
} | |
return this; | |
}, | |
setAxisAngleFromRotationMatrix: function ( m ) { | |
// http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToAngle/index.htm | |
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
var angle, x, y, z, // variables for result | |
epsilon = 0.01, // margin to allow for rounding errors | |
epsilon2 = 0.1, // margin to distinguish between 0 and 180 degrees | |
te = m.elements, | |
m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ], | |
m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ], | |
m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; | |
if ( ( Math.abs( m12 - m21 ) < epsilon ) | |
&& ( Math.abs( m13 - m31 ) < epsilon ) | |
&& ( Math.abs( m23 - m32 ) < epsilon ) ) { | |
// singularity found | |
// first check for identity matrix which must have +1 for all terms | |
// in leading diagonal and zero in other terms | |
if ( ( Math.abs( m12 + m21 ) < epsilon2 ) | |
&& ( Math.abs( m13 + m31 ) < epsilon2 ) | |
&& ( Math.abs( m23 + m32 ) < epsilon2 ) | |
&& ( Math.abs( m11 + m22 + m33 - 3 ) < epsilon2 ) ) { | |
// this singularity is identity matrix so angle = 0 | |
this.set( 1, 0, 0, 0 ); | |
return this; // zero angle, arbitrary axis | |
} | |
// otherwise this singularity is angle = 180 | |
angle = Math.PI; | |
var xx = ( m11 + 1 ) / 2; | |
var yy = ( m22 + 1 ) / 2; | |
var zz = ( m33 + 1 ) / 2; | |
var xy = ( m12 + m21 ) / 4; | |
var xz = ( m13 + m31 ) / 4; | |
var yz = ( m23 + m32 ) / 4; | |
if ( ( xx > yy ) && ( xx > zz ) ) { | |
// m11 is the largest diagonal term | |
if ( xx < epsilon ) { | |
x = 0; | |
y = 0.707106781; | |
z = 0.707106781; | |
} else { | |
x = Math.sqrt( xx ); | |
y = xy / x; | |
z = xz / x; | |
} | |
} else if ( yy > zz ) { | |
// m22 is the largest diagonal term | |
if ( yy < epsilon ) { | |
x = 0.707106781; | |
y = 0; | |
z = 0.707106781; | |
} else { | |
y = Math.sqrt( yy ); | |
x = xy / y; | |
z = yz / y; | |
} | |
} else { | |
// m33 is the largest diagonal term so base result on this | |
if ( zz < epsilon ) { | |
x = 0.707106781; | |
y = 0.707106781; | |
z = 0; | |
} else { | |
z = Math.sqrt( zz ); | |
x = xz / z; | |
y = yz / z; | |
} | |
} | |
this.set( x, y, z, angle ); | |
return this; // return 180 deg rotation | |
} | |
// as we have reached here there are no singularities so we can handle normally | |
var s = Math.sqrt( ( m32 - m23 ) * ( m32 - m23 ) | |
+ ( m13 - m31 ) * ( m13 - m31 ) | |
+ ( m21 - m12 ) * ( m21 - m12 ) ); // used to normalize | |
if ( Math.abs( s ) < 0.001 ) s = 1; | |
// prevent divide by zero, should not happen if matrix is orthogonal and should be | |
// caught by singularity test above, but I've left it in just in case | |
this.x = ( m32 - m23 ) / s; | |
this.y = ( m13 - m31 ) / s; | |
this.z = ( m21 - m12 ) / s; | |
this.w = Math.acos( ( m11 + m22 + m33 - 1 ) / 2 ); | |
return this; | |
}, | |
min: function ( v ) { | |
this.x = Math.min( this.x, v.x ); | |
this.y = Math.min( this.y, v.y ); | |
this.z = Math.min( this.z, v.z ); | |
this.w = Math.min( this.w, v.w ); | |
return this; | |
}, | |
max: function ( v ) { | |
this.x = Math.max( this.x, v.x ); | |
this.y = Math.max( this.y, v.y ); | |
this.z = Math.max( this.z, v.z ); | |
this.w = Math.max( this.w, v.w ); | |
return this; | |
}, | |
clamp: function ( min, max ) { | |
// This function assumes min < max, if this assumption isn't true it will not operate correctly | |
this.x = Math.max( min.x, Math.min( max.x, this.x ) ); | |
this.y = Math.max( min.y, Math.min( max.y, this.y ) ); | |
this.z = Math.max( min.z, Math.min( max.z, this.z ) ); | |
this.w = Math.max( min.w, Math.min( max.w, this.w ) ); | |
return this; | |
}, | |
clampScalar: function () { | |
var min, max; | |
return function clampScalar( minVal, maxVal ) { | |
if ( min === undefined ) { | |
min = new THREE.Vector4(); | |
max = new THREE.Vector4(); | |
} | |
min.set( minVal, minVal, minVal, minVal ); | |
max.set( maxVal, maxVal, maxVal, maxVal ); | |
return this.clamp( min, max ); | |
}; | |
}(), | |
floor: function () { | |
this.x = Math.floor( this.x ); | |
this.y = Math.floor( this.y ); | |
this.z = Math.floor( this.z ); | |
this.w = Math.floor( this.w ); | |
return this; | |
}, | |
ceil: function () { | |
this.x = Math.ceil( this.x ); | |
this.y = Math.ceil( this.y ); | |
this.z = Math.ceil( this.z ); | |
this.w = Math.ceil( this.w ); | |
return this; | |
}, | |
round: function () { | |
this.x = Math.round( this.x ); | |
this.y = Math.round( this.y ); | |
this.z = Math.round( this.z ); | |
this.w = Math.round( this.w ); | |
return this; | |
}, | |
roundToZero: function () { | |
this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x ); | |
this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y ); | |
this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z ); | |
this.w = ( this.w < 0 ) ? Math.ceil( this.w ) : Math.floor( this.w ); | |
return this; | |
}, | |
negate: function () { | |
this.x = - this.x; | |
this.y = - this.y; | |
this.z = - this.z; | |
this.w = - this.w; | |
return this; | |
}, | |
dot: function ( v ) { | |
return this.x * v.x + this.y * v.y + this.z * v.z + this.w * v.w; | |
}, | |
lengthSq: function () { | |
return this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w; | |
}, | |
length: function () { | |
return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w ); | |
}, | |
lengthManhattan: function () { | |
return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z ) + Math.abs( this.w ); | |
}, | |
normalize: function () { | |
return this.divideScalar( this.length() ); | |
}, | |
setLength: function ( length ) { | |
return this.multiplyScalar( length / this.length() ); | |
}, | |
lerp: function ( v, alpha ) { | |
this.x += ( v.x - this.x ) * alpha; | |
this.y += ( v.y - this.y ) * alpha; | |
this.z += ( v.z - this.z ) * alpha; | |
this.w += ( v.w - this.w ) * alpha; | |
return this; | |
}, | |
lerpVectors: function ( v1, v2, alpha ) { | |
this.subVectors( v2, v1 ).multiplyScalar( alpha ).add( v1 ); | |
return this; | |
}, | |
equals: function ( v ) { | |
return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) && ( v.w === this.w ) ); | |
}, | |
fromArray: function ( array, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.x = array[ offset ]; | |
this.y = array[ offset + 1 ]; | |
this.z = array[ offset + 2 ]; | |
this.w = array[ offset + 3 ]; | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this.x; | |
array[ offset + 1 ] = this.y; | |
array[ offset + 2 ] = this.z; | |
array[ offset + 3 ] = this.w; | |
return array; | |
}, | |
fromAttribute: function ( attribute, index, offset ) { | |
if ( offset === undefined ) offset = 0; | |
index = index * attribute.itemSize + offset; | |
this.x = attribute.array[ index ]; | |
this.y = attribute.array[ index + 1 ]; | |
this.z = attribute.array[ index + 2 ]; | |
this.w = attribute.array[ index + 3 ]; | |
return this; | |
} | |
}; | |
// File:src/math/Euler.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Euler = function ( x, y, z, order ) { | |
this._x = x || 0; | |
this._y = y || 0; | |
this._z = z || 0; | |
this._order = order || THREE.Euler.DefaultOrder; | |
}; | |
THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ]; | |
THREE.Euler.DefaultOrder = 'XYZ'; | |
THREE.Euler.prototype = { | |
constructor: THREE.Euler, | |
get x () { | |
return this._x; | |
}, | |
set x ( value ) { | |
this._x = value; | |
this.onChangeCallback(); | |
}, | |
get y () { | |
return this._y; | |
}, | |
set y ( value ) { | |
this._y = value; | |
this.onChangeCallback(); | |
}, | |
get z () { | |
return this._z; | |
}, | |
set z ( value ) { | |
this._z = value; | |
this.onChangeCallback(); | |
}, | |
get order () { | |
return this._order; | |
}, | |
set order ( value ) { | |
this._order = value; | |
this.onChangeCallback(); | |
}, | |
set: function ( x, y, z, order ) { | |
this._x = x; | |
this._y = y; | |
this._z = z; | |
this._order = order || this._order; | |
this.onChangeCallback(); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor( this._x, this._y, this._z, this._order ); | |
}, | |
copy: function ( euler ) { | |
this._x = euler._x; | |
this._y = euler._y; | |
this._z = euler._z; | |
this._order = euler._order; | |
this.onChangeCallback(); | |
return this; | |
}, | |
setFromRotationMatrix: function ( m, order, update ) { | |
var clamp = THREE.Math.clamp; | |
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
var te = m.elements; | |
var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ]; | |
var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ]; | |
var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ]; | |
order = order || this._order; | |
if ( order === 'XYZ' ) { | |
this._y = Math.asin( clamp( m13, - 1, 1 ) ); | |
if ( Math.abs( m13 ) < 0.99999 ) { | |
this._x = Math.atan2( - m23, m33 ); | |
this._z = Math.atan2( - m12, m11 ); | |
} else { | |
this._x = Math.atan2( m32, m22 ); | |
this._z = 0; | |
} | |
} else if ( order === 'YXZ' ) { | |
this._x = Math.asin( - clamp( m23, - 1, 1 ) ); | |
if ( Math.abs( m23 ) < 0.99999 ) { | |
this._y = Math.atan2( m13, m33 ); | |
this._z = Math.atan2( m21, m22 ); | |
} else { | |
this._y = Math.atan2( - m31, m11 ); | |
this._z = 0; | |
} | |
} else if ( order === 'ZXY' ) { | |
this._x = Math.asin( clamp( m32, - 1, 1 ) ); | |
if ( Math.abs( m32 ) < 0.99999 ) { | |
this._y = Math.atan2( - m31, m33 ); | |
this._z = Math.atan2( - m12, m22 ); | |
} else { | |
this._y = 0; | |
this._z = Math.atan2( m21, m11 ); | |
} | |
} else if ( order === 'ZYX' ) { | |
this._y = Math.asin( - clamp( m31, - 1, 1 ) ); | |
if ( Math.abs( m31 ) < 0.99999 ) { | |
this._x = Math.atan2( m32, m33 ); | |
this._z = Math.atan2( m21, m11 ); | |
} else { | |
this._x = 0; | |
this._z = Math.atan2( - m12, m22 ); | |
} | |
} else if ( order === 'YZX' ) { | |
this._z = Math.asin( clamp( m21, - 1, 1 ) ); | |
if ( Math.abs( m21 ) < 0.99999 ) { | |
this._x = Math.atan2( - m23, m22 ); | |
this._y = Math.atan2( - m31, m11 ); | |
} else { | |
this._x = 0; | |
this._y = Math.atan2( m13, m33 ); | |
} | |
} else if ( order === 'XZY' ) { | |
this._z = Math.asin( - clamp( m12, - 1, 1 ) ); | |
if ( Math.abs( m12 ) < 0.99999 ) { | |
this._x = Math.atan2( m32, m22 ); | |
this._y = Math.atan2( m13, m11 ); | |
} else { | |
this._x = Math.atan2( - m23, m33 ); | |
this._y = 0; | |
} | |
} else { | |
console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order ) | |
} | |
this._order = order; | |
if ( update !== false ) this.onChangeCallback(); | |
return this; | |
}, | |
setFromQuaternion: function () { | |
var matrix; | |
return function ( q, order, update ) { | |
if ( matrix === undefined ) matrix = new THREE.Matrix4(); | |
matrix.makeRotationFromQuaternion( q ); | |
this.setFromRotationMatrix( matrix, order, update ); | |
return this; | |
}; | |
}(), | |
setFromVector3: function ( v, order ) { | |
return this.set( v.x, v.y, v.z, order || this._order ); | |
}, | |
reorder: function () { | |
// WARNING: this discards revolution information -bhouston | |
var q = new THREE.Quaternion(); | |
return function ( newOrder ) { | |
q.setFromEuler( this ); | |
this.setFromQuaternion( q, newOrder ); | |
}; | |
}(), | |
equals: function ( euler ) { | |
return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order ); | |
}, | |
fromArray: function ( array ) { | |
this._x = array[ 0 ]; | |
this._y = array[ 1 ]; | |
this._z = array[ 2 ]; | |
if ( array[ 3 ] !== undefined ) this._order = array[ 3 ]; | |
this.onChangeCallback(); | |
return this; | |
}, | |
toArray: function ( array, offset ) { | |
if ( array === undefined ) array = []; | |
if ( offset === undefined ) offset = 0; | |
array[ offset ] = this._x; | |
array[ offset + 1 ] = this._y; | |
array[ offset + 2 ] = this._z; | |
array[ offset + 3 ] = this._order; | |
return array; | |
}, | |
toVector3: function ( optionalResult ) { | |
if ( optionalResult ) { | |
return optionalResult.set( this._x, this._y, this._z ); | |
} else { | |
return new THREE.Vector3( this._x, this._y, this._z ); | |
} | |
}, | |
onChange: function ( callback ) { | |
this.onChangeCallback = callback; | |
return this; | |
}, | |
onChangeCallback: function () {} | |
}; | |
// File:src/math/Line3.js | |
/** | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Line3 = function ( start, end ) { | |
this.start = ( start !== undefined ) ? start : new THREE.Vector3(); | |
this.end = ( end !== undefined ) ? end : new THREE.Vector3(); | |
}; | |
THREE.Line3.prototype = { | |
constructor: THREE.Line3, | |
set: function ( start, end ) { | |
this.start.copy( start ); | |
this.end.copy( end ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( line ) { | |
this.start.copy( line.start ); | |
this.end.copy( line.end ); | |
return this; | |
}, | |
center: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.addVectors( this.start, this.end ).multiplyScalar( 0.5 ); | |
}, | |
delta: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.subVectors( this.end, this.start ); | |
}, | |
distanceSq: function () { | |
return this.start.distanceToSquared( this.end ); | |
}, | |
distance: function () { | |
return this.start.distanceTo( this.end ); | |
}, | |
at: function ( t, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return this.delta( result ).multiplyScalar( t ).add( this.start ); | |
}, | |
closestPointToPointParameter: function () { | |
var startP = new THREE.Vector3(); | |
var startEnd = new THREE.Vector3(); | |
return function ( point, clampToLine ) { | |
startP.subVectors( point, this.start ); | |
startEnd.subVectors( this.end, this.start ); | |
var startEnd2 = startEnd.dot( startEnd ); | |
var startEnd_startP = startEnd.dot( startP ); | |
var t = startEnd_startP / startEnd2; | |
if ( clampToLine ) { | |
t = THREE.Math.clamp( t, 0, 1 ); | |
} | |
return t; | |
}; | |
}(), | |
closestPointToPoint: function ( point, clampToLine, optionalTarget ) { | |
var t = this.closestPointToPointParameter( point, clampToLine ); | |
var result = optionalTarget || new THREE.Vector3(); | |
return this.delta( result ).multiplyScalar( t ).add( this.start ); | |
}, | |
applyMatrix4: function ( matrix ) { | |
this.start.applyMatrix4( matrix ); | |
this.end.applyMatrix4( matrix ); | |
return this; | |
}, | |
equals: function ( line ) { | |
return line.start.equals( this.start ) && line.end.equals( this.end ); | |
} | |
}; | |
// File:src/math/Box2.js | |
/** | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Box2 = function ( min, max ) { | |
this.min = ( min !== undefined ) ? min : new THREE.Vector2( + Infinity, + Infinity ); | |
this.max = ( max !== undefined ) ? max : new THREE.Vector2( - Infinity, - Infinity ); | |
}; | |
THREE.Box2.prototype = { | |
constructor: THREE.Box2, | |
set: function ( min, max ) { | |
this.min.copy( min ); | |
this.max.copy( max ); | |
return this; | |
}, | |
setFromPoints: function ( points ) { | |
this.makeEmpty(); | |
for ( var i = 0, il = points.length; i < il; i ++ ) { | |
this.expandByPoint( points[ i ] ); | |
} | |
return this; | |
}, | |
setFromCenterAndSize: function () { | |
var v1 = new THREE.Vector2(); | |
return function ( center, size ) { | |
var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); | |
this.min.copy( center ).sub( halfSize ); | |
this.max.copy( center ).add( halfSize ); | |
return this; | |
}; | |
}(), | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( box ) { | |
this.min.copy( box.min ); | |
this.max.copy( box.max ); | |
return this; | |
}, | |
makeEmpty: function () { | |
this.min.x = this.min.y = + Infinity; | |
this.max.x = this.max.y = - Infinity; | |
return this; | |
}, | |
isEmpty: function () { | |
// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes | |
return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ); | |
}, | |
center: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector2(); | |
return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); | |
}, | |
size: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector2(); | |
return result.subVectors( this.max, this.min ); | |
}, | |
expandByPoint: function ( point ) { | |
this.min.min( point ); | |
this.max.max( point ); | |
return this; | |
}, | |
expandByVector: function ( vector ) { | |
this.min.sub( vector ); | |
this.max.add( vector ); | |
return this; | |
}, | |
expandByScalar: function ( scalar ) { | |
this.min.addScalar( - scalar ); | |
this.max.addScalar( scalar ); | |
return this; | |
}, | |
containsPoint: function ( point ) { | |
if ( point.x < this.min.x || point.x > this.max.x || | |
point.y < this.min.y || point.y > this.max.y ) { | |
return false; | |
} | |
return true; | |
}, | |
containsBox: function ( box ) { | |
if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && | |
( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) ) { | |
return true; | |
} | |
return false; | |
}, | |
getParameter: function ( point, optionalTarget ) { | |
// This can potentially have a divide by zero if the box | |
// has a size dimension of 0. | |
var result = optionalTarget || new THREE.Vector2(); | |
return result.set( | |
( point.x - this.min.x ) / ( this.max.x - this.min.x ), | |
( point.y - this.min.y ) / ( this.max.y - this.min.y ) | |
); | |
}, | |
intersectsBox: function ( box ) { | |
// using 6 splitting planes to rule out intersections. | |
if ( box.max.x < this.min.x || box.min.x > this.max.x || | |
box.max.y < this.min.y || box.min.y > this.max.y ) { | |
return false; | |
} | |
return true; | |
}, | |
clampPoint: function ( point, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector2(); | |
return result.copy( point ).clamp( this.min, this.max ); | |
}, | |
distanceToPoint: function () { | |
var v1 = new THREE.Vector2(); | |
return function ( point ) { | |
var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); | |
return clampedPoint.sub( point ).length(); | |
}; | |
}(), | |
intersect: function ( box ) { | |
this.min.max( box.min ); | |
this.max.min( box.max ); | |
return this; | |
}, | |
union: function ( box ) { | |
this.min.min( box.min ); | |
this.max.max( box.max ); | |
return this; | |
}, | |
translate: function ( offset ) { | |
this.min.add( offset ); | |
this.max.add( offset ); | |
return this; | |
}, | |
equals: function ( box ) { | |
return box.min.equals( this.min ) && box.max.equals( this.max ); | |
} | |
}; | |
// File:src/math/Box3.js | |
/** | |
* @author bhouston / http://clara.io | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.Box3 = function ( min, max ) { | |
this.min = ( min !== undefined ) ? min : new THREE.Vector3( + Infinity, + Infinity, + Infinity ); | |
this.max = ( max !== undefined ) ? max : new THREE.Vector3( - Infinity, - Infinity, - Infinity ); | |
}; | |
THREE.Box3.prototype = { | |
constructor: THREE.Box3, | |
set: function ( min, max ) { | |
this.min.copy( min ); | |
this.max.copy( max ); | |
return this; | |
}, | |
setFromArray: function ( array ) { | |
this.makeEmpty(); | |
var minX = + Infinity; | |
var minY = + Infinity; | |
var minZ = + Infinity; | |
var maxX = - Infinity; | |
var maxY = - Infinity; | |
var maxZ = - Infinity; | |
for ( var i = 0, il = array.length; i < il; i += 3 ) { | |
var x = array[ i ]; | |
var y = array[ i + 1 ]; | |
var z = array[ i + 2 ]; | |
if ( x < minX ) minX = x; | |
if ( y < minY ) minY = y; | |
if ( z < minZ ) minZ = z; | |
if ( x > maxX ) maxX = x; | |
if ( y > maxY ) maxY = y; | |
if ( z > maxZ ) maxZ = z; | |
} | |
this.min.set( minX, minY, minZ ); | |
this.max.set( maxX, maxY, maxZ ); | |
}, | |
setFromPoints: function ( points ) { | |
this.makeEmpty(); | |
for ( var i = 0, il = points.length; i < il; i ++ ) { | |
this.expandByPoint( points[ i ] ); | |
} | |
return this; | |
}, | |
setFromCenterAndSize: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( center, size ) { | |
var halfSize = v1.copy( size ).multiplyScalar( 0.5 ); | |
this.min.copy( center ).sub( halfSize ); | |
this.max.copy( center ).add( halfSize ); | |
return this; | |
}; | |
}(), | |
setFromObject: function () { | |
// Computes the world-axis-aligned bounding box of an object (including its children), | |
// accounting for both the object's, and children's, world transforms | |
var box; | |
return function ( object ) { | |
if ( box === undefined ) box = new THREE.Box3(); | |
var scope = this; | |
this.makeEmpty(); | |
object.updateMatrixWorld( true ); | |
object.traverse( function ( node ) { | |
var geometry = node.geometry; | |
if ( geometry !== undefined ) { | |
if ( geometry.boundingBox === null ) { | |
geometry.computeBoundingBox(); | |
} | |
box.copy( geometry.boundingBox ); | |
box.applyMatrix4( node.matrixWorld ); | |
scope.union( box ); | |
} | |
} ); | |
return this; | |
}; | |
}(), | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( box ) { | |
this.min.copy( box.min ); | |
this.max.copy( box.max ); | |
return this; | |
}, | |
makeEmpty: function () { | |
this.min.x = this.min.y = this.min.z = + Infinity; | |
this.max.x = this.max.y = this.max.z = - Infinity; | |
return this; | |
}, | |
isEmpty: function () { | |
// this is a more robust check for empty than ( volume <= 0 ) because volume can get positive with two negative axes | |
return ( this.max.x < this.min.x ) || ( this.max.y < this.min.y ) || ( this.max.z < this.min.z ); | |
}, | |
center: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.addVectors( this.min, this.max ).multiplyScalar( 0.5 ); | |
}, | |
size: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.subVectors( this.max, this.min ); | |
}, | |
expandByPoint: function ( point ) { | |
this.min.min( point ); | |
this.max.max( point ); | |
return this; | |
}, | |
expandByVector: function ( vector ) { | |
this.min.sub( vector ); | |
this.max.add( vector ); | |
return this; | |
}, | |
expandByScalar: function ( scalar ) { | |
this.min.addScalar( - scalar ); | |
this.max.addScalar( scalar ); | |
return this; | |
}, | |
containsPoint: function ( point ) { | |
if ( point.x < this.min.x || point.x > this.max.x || | |
point.y < this.min.y || point.y > this.max.y || | |
point.z < this.min.z || point.z > this.max.z ) { | |
return false; | |
} | |
return true; | |
}, | |
containsBox: function ( box ) { | |
if ( ( this.min.x <= box.min.x ) && ( box.max.x <= this.max.x ) && | |
( this.min.y <= box.min.y ) && ( box.max.y <= this.max.y ) && | |
( this.min.z <= box.min.z ) && ( box.max.z <= this.max.z ) ) { | |
return true; | |
} | |
return false; | |
}, | |
getParameter: function ( point, optionalTarget ) { | |
// This can potentially have a divide by zero if the box | |
// has a size dimension of 0. | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.set( | |
( point.x - this.min.x ) / ( this.max.x - this.min.x ), | |
( point.y - this.min.y ) / ( this.max.y - this.min.y ), | |
( point.z - this.min.z ) / ( this.max.z - this.min.z ) | |
); | |
}, | |
intersectsBox: function ( box ) { | |
// using 6 splitting planes to rule out intersections. | |
if ( box.max.x < this.min.x || box.min.x > this.max.x || | |
box.max.y < this.min.y || box.min.y > this.max.y || | |
box.max.z < this.min.z || box.min.z > this.max.z ) { | |
return false; | |
} | |
return true; | |
}, | |
intersectsSphere: ( function () { | |
var closestPoint; | |
return function intersectsSphere( sphere ) { | |
if ( closestPoint === undefined ) closestPoint = new THREE.Vector3(); | |
// Find the point on the AABB closest to the sphere center. | |
this.clampPoint( sphere.center, closestPoint ); | |
// If that point is inside the sphere, the AABB and sphere intersect. | |
return closestPoint.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius ); | |
}; | |
} )(), | |
intersectsPlane: function ( plane ) { | |
// We compute the minimum and maximum dot product values. If those values | |
// are on the same side (back or front) of the plane, then there is no intersection. | |
var min, max; | |
if ( plane.normal.x > 0 ) { | |
min = plane.normal.x * this.min.x; | |
max = plane.normal.x * this.max.x; | |
} else { | |
min = plane.normal.x * this.max.x; | |
max = plane.normal.x * this.min.x; | |
} | |
if ( plane.normal.y > 0 ) { | |
min += plane.normal.y * this.min.y; | |
max += plane.normal.y * this.max.y; | |
} else { | |
min += plane.normal.y * this.max.y; | |
max += plane.normal.y * this.min.y; | |
} | |
if ( plane.normal.z > 0 ) { | |
min += plane.normal.z * this.min.z; | |
max += plane.normal.z * this.max.z; | |
} else { | |
min += plane.normal.z * this.max.z; | |
max += plane.normal.z * this.min.z; | |
} | |
return ( min <= plane.constant && max >= plane.constant ); | |
}, | |
clampPoint: function ( point, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.copy( point ).clamp( this.min, this.max ); | |
}, | |
distanceToPoint: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( point ) { | |
var clampedPoint = v1.copy( point ).clamp( this.min, this.max ); | |
return clampedPoint.sub( point ).length(); | |
}; | |
}(), | |
getBoundingSphere: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Sphere(); | |
result.center = this.center(); | |
result.radius = this.size( v1 ).length() * 0.5; | |
return result; | |
}; | |
}(), | |
intersect: function ( box ) { | |
this.min.max( box.min ); | |
this.max.min( box.max ); | |
return this; | |
}, | |
union: function ( box ) { | |
this.min.min( box.min ); | |
this.max.max( box.max ); | |
return this; | |
}, | |
applyMatrix4: function () { | |
var points = [ | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3(), | |
new THREE.Vector3() | |
]; | |
return function ( matrix ) { | |
// NOTE: I am using a binary pattern to specify all 2^3 combinations below | |
points[ 0 ].set( this.min.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 000 | |
points[ 1 ].set( this.min.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 001 | |
points[ 2 ].set( this.min.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 010 | |
points[ 3 ].set( this.min.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 011 | |
points[ 4 ].set( this.max.x, this.min.y, this.min.z ).applyMatrix4( matrix ); // 100 | |
points[ 5 ].set( this.max.x, this.min.y, this.max.z ).applyMatrix4( matrix ); // 101 | |
points[ 6 ].set( this.max.x, this.max.y, this.min.z ).applyMatrix4( matrix ); // 110 | |
points[ 7 ].set( this.max.x, this.max.y, this.max.z ).applyMatrix4( matrix ); // 111 | |
this.makeEmpty(); | |
this.setFromPoints( points ); | |
return this; | |
}; | |
}(), | |
translate: function ( offset ) { | |
this.min.add( offset ); | |
this.max.add( offset ); | |
return this; | |
}, | |
equals: function ( box ) { | |
return box.min.equals( this.min ) && box.max.equals( this.max ); | |
} | |
}; | |
// File:src/math/Matrix3.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Matrix3 = function () { | |
this.elements = new Float32Array( [ | |
1, 0, 0, | |
0, 1, 0, | |
0, 0, 1 | |
] ); | |
if ( arguments.length > 0 ) { | |
console.error( 'THREE.Matrix3: the constructor no longer reads arguments. use .set() instead.' ); | |
} | |
}; | |
THREE.Matrix3.prototype = { | |
constructor: THREE.Matrix3, | |
set: function ( n11, n12, n13, n21, n22, n23, n31, n32, n33 ) { | |
var te = this.elements; | |
te[ 0 ] = n11; te[ 3 ] = n12; te[ 6 ] = n13; | |
te[ 1 ] = n21; te[ 4 ] = n22; te[ 7 ] = n23; | |
te[ 2 ] = n31; te[ 5 ] = n32; te[ 8 ] = n33; | |
return this; | |
}, | |
identity: function () { | |
this.set( | |
1, 0, 0, | |
0, 1, 0, | |
0, 0, 1 | |
); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().fromArray( this.elements ); | |
}, | |
copy: function ( m ) { | |
var me = m.elements; | |
this.set( | |
me[ 0 ], me[ 3 ], me[ 6 ], | |
me[ 1 ], me[ 4 ], me[ 7 ], | |
me[ 2 ], me[ 5 ], me[ 8 ] | |
); | |
return this; | |
}, | |
applyToVector3Array: function () { | |
var v1; | |
return function ( array, offset, length ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
if ( offset === undefined ) offset = 0; | |
if ( length === undefined ) length = array.length; | |
for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { | |
v1.fromArray( array, j ); | |
v1.applyMatrix3( this ); | |
v1.toArray( array, j ); | |
} | |
return array; | |
}; | |
}(), | |
applyToBuffer: function () { | |
var v1; | |
return function applyToBuffer( buffer, offset, length ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
if ( offset === undefined ) offset = 0; | |
if ( length === undefined ) length = buffer.length / buffer.itemSize; | |
for ( var i = 0, j = offset; i < length; i ++, j ++ ) { | |
v1.x = buffer.getX( j ); | |
v1.y = buffer.getY( j ); | |
v1.z = buffer.getZ( j ); | |
v1.applyMatrix3( this ); | |
buffer.setXYZ( v1.x, v1.y, v1.z ); | |
} | |
return buffer; | |
}; | |
}(), | |
multiplyScalar: function ( s ) { | |
var te = this.elements; | |
te[ 0 ] *= s; te[ 3 ] *= s; te[ 6 ] *= s; | |
te[ 1 ] *= s; te[ 4 ] *= s; te[ 7 ] *= s; | |
te[ 2 ] *= s; te[ 5 ] *= s; te[ 8 ] *= s; | |
return this; | |
}, | |
determinant: function () { | |
var te = this.elements; | |
var a = te[ 0 ], b = te[ 1 ], c = te[ 2 ], | |
d = te[ 3 ], e = te[ 4 ], f = te[ 5 ], | |
g = te[ 6 ], h = te[ 7 ], i = te[ 8 ]; | |
return a * e * i - a * f * h - b * d * i + b * f * g + c * d * h - c * e * g; | |
}, | |
getInverse: function ( matrix, throwOnDegenerate ) { | |
// input: THREE.Matrix4 | |
// ( based on http://code.google.com/p/webgl-mjs/ ) | |
var me = matrix.elements; | |
var te = this.elements; | |
te[ 0 ] = me[ 10 ] * me[ 5 ] - me[ 6 ] * me[ 9 ]; | |
te[ 1 ] = - me[ 10 ] * me[ 1 ] + me[ 2 ] * me[ 9 ]; | |
te[ 2 ] = me[ 6 ] * me[ 1 ] - me[ 2 ] * me[ 5 ]; | |
te[ 3 ] = - me[ 10 ] * me[ 4 ] + me[ 6 ] * me[ 8 ]; | |
te[ 4 ] = me[ 10 ] * me[ 0 ] - me[ 2 ] * me[ 8 ]; | |
te[ 5 ] = - me[ 6 ] * me[ 0 ] + me[ 2 ] * me[ 4 ]; | |
te[ 6 ] = me[ 9 ] * me[ 4 ] - me[ 5 ] * me[ 8 ]; | |
te[ 7 ] = - me[ 9 ] * me[ 0 ] + me[ 1 ] * me[ 8 ]; | |
te[ 8 ] = me[ 5 ] * me[ 0 ] - me[ 1 ] * me[ 4 ]; | |
var det = me[ 0 ] * te[ 0 ] + me[ 1 ] * te[ 3 ] + me[ 2 ] * te[ 6 ]; | |
// no inverse | |
if ( det === 0 ) { | |
var msg = "THREE.Matrix3.getInverse(): can't invert matrix, determinant is 0"; | |
if ( throwOnDegenerate || false ) { | |
throw new Error( msg ); | |
} else { | |
console.warn( msg ); | |
} | |
this.identity(); | |
return this; | |
} | |
this.multiplyScalar( 1.0 / det ); | |
return this; | |
}, | |
transpose: function () { | |
var tmp, m = this.elements; | |
tmp = m[ 1 ]; m[ 1 ] = m[ 3 ]; m[ 3 ] = tmp; | |
tmp = m[ 2 ]; m[ 2 ] = m[ 6 ]; m[ 6 ] = tmp; | |
tmp = m[ 5 ]; m[ 5 ] = m[ 7 ]; m[ 7 ] = tmp; | |
return this; | |
}, | |
flattenToArrayOffset: function ( array, offset ) { | |
var te = this.elements; | |
array[ offset ] = te[ 0 ]; | |
array[ offset + 1 ] = te[ 1 ]; | |
array[ offset + 2 ] = te[ 2 ]; | |
array[ offset + 3 ] = te[ 3 ]; | |
array[ offset + 4 ] = te[ 4 ]; | |
array[ offset + 5 ] = te[ 5 ]; | |
array[ offset + 6 ] = te[ 6 ]; | |
array[ offset + 7 ] = te[ 7 ]; | |
array[ offset + 8 ] = te[ 8 ]; | |
return array; | |
}, | |
getNormalMatrix: function ( m ) { | |
// input: THREE.Matrix4 | |
this.getInverse( m ).transpose(); | |
return this; | |
}, | |
transposeIntoArray: function ( r ) { | |
var m = this.elements; | |
r[ 0 ] = m[ 0 ]; | |
r[ 1 ] = m[ 3 ]; | |
r[ 2 ] = m[ 6 ]; | |
r[ 3 ] = m[ 1 ]; | |
r[ 4 ] = m[ 4 ]; | |
r[ 5 ] = m[ 7 ]; | |
r[ 6 ] = m[ 2 ]; | |
r[ 7 ] = m[ 5 ]; | |
r[ 8 ] = m[ 8 ]; | |
return this; | |
}, | |
fromArray: function ( array ) { | |
this.elements.set( array ); | |
return this; | |
}, | |
toArray: function () { | |
var te = this.elements; | |
return [ | |
te[ 0 ], te[ 1 ], te[ 2 ], | |
te[ 3 ], te[ 4 ], te[ 5 ], | |
te[ 6 ], te[ 7 ], te[ 8 ] | |
]; | |
} | |
}; | |
// File:src/math/Matrix4.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author supereggbert / http://www.paulbrunt.co.uk/ | |
* @author philogb / http://blog.thejit.org/ | |
* @author jordi_ros / http://plattsoft.com | |
* @author D1plo1d / http://github.com/D1plo1d | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author timknip / http://www.floorplanner.com/ | |
* @author bhouston / http://clara.io | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.Matrix4 = function () { | |
this.elements = new Float32Array( [ | |
1, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1 | |
] ); | |
if ( arguments.length > 0 ) { | |
console.error( 'THREE.Matrix4: the constructor no longer reads arguments. use .set() instead.' ); | |
} | |
}; | |
THREE.Matrix4.prototype = { | |
constructor: THREE.Matrix4, | |
set: function ( n11, n12, n13, n14, n21, n22, n23, n24, n31, n32, n33, n34, n41, n42, n43, n44 ) { | |
var te = this.elements; | |
te[ 0 ] = n11; te[ 4 ] = n12; te[ 8 ] = n13; te[ 12 ] = n14; | |
te[ 1 ] = n21; te[ 5 ] = n22; te[ 9 ] = n23; te[ 13 ] = n24; | |
te[ 2 ] = n31; te[ 6 ] = n32; te[ 10 ] = n33; te[ 14 ] = n34; | |
te[ 3 ] = n41; te[ 7 ] = n42; te[ 11 ] = n43; te[ 15 ] = n44; | |
return this; | |
}, | |
identity: function () { | |
this.set( | |
1, 0, 0, 0, | |
0, 1, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
clone: function () { | |
return new THREE.Matrix4().fromArray( this.elements ); | |
}, | |
copy: function ( m ) { | |
this.elements.set( m.elements ); | |
return this; | |
}, | |
copyPosition: function ( m ) { | |
var te = this.elements; | |
var me = m.elements; | |
te[ 12 ] = me[ 12 ]; | |
te[ 13 ] = me[ 13 ]; | |
te[ 14 ] = me[ 14 ]; | |
return this; | |
}, | |
extractBasis: function ( xAxis, yAxis, zAxis ) { | |
var te = this.elements; | |
xAxis.set( te[ 0 ], te[ 1 ], te[ 2 ] ); | |
yAxis.set( te[ 4 ], te[ 5 ], te[ 6 ] ); | |
zAxis.set( te[ 8 ], te[ 9 ], te[ 10 ] ); | |
return this; | |
}, | |
makeBasis: function ( xAxis, yAxis, zAxis ) { | |
this.set( | |
xAxis.x, yAxis.x, zAxis.x, 0, | |
xAxis.y, yAxis.y, zAxis.y, 0, | |
xAxis.z, yAxis.z, zAxis.z, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
extractRotation: function () { | |
var v1; | |
return function ( m ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
var te = this.elements; | |
var me = m.elements; | |
var scaleX = 1 / v1.set( me[ 0 ], me[ 1 ], me[ 2 ] ).length(); | |
var scaleY = 1 / v1.set( me[ 4 ], me[ 5 ], me[ 6 ] ).length(); | |
var scaleZ = 1 / v1.set( me[ 8 ], me[ 9 ], me[ 10 ] ).length(); | |
te[ 0 ] = me[ 0 ] * scaleX; | |
te[ 1 ] = me[ 1 ] * scaleX; | |
te[ 2 ] = me[ 2 ] * scaleX; | |
te[ 4 ] = me[ 4 ] * scaleY; | |
te[ 5 ] = me[ 5 ] * scaleY; | |
te[ 6 ] = me[ 6 ] * scaleY; | |
te[ 8 ] = me[ 8 ] * scaleZ; | |
te[ 9 ] = me[ 9 ] * scaleZ; | |
te[ 10 ] = me[ 10 ] * scaleZ; | |
return this; | |
}; | |
}(), | |
makeRotationFromEuler: function ( euler ) { | |
if ( euler instanceof THREE.Euler === false ) { | |
console.error( 'THREE.Matrix: .makeRotationFromEuler() now expects a Euler rotation rather than a Vector3 and order.' ); | |
} | |
var te = this.elements; | |
var x = euler.x, y = euler.y, z = euler.z; | |
var a = Math.cos( x ), b = Math.sin( x ); | |
var c = Math.cos( y ), d = Math.sin( y ); | |
var e = Math.cos( z ), f = Math.sin( z ); | |
if ( euler.order === 'XYZ' ) { | |
var ae = a * e, af = a * f, be = b * e, bf = b * f; | |
te[ 0 ] = c * e; | |
te[ 4 ] = - c * f; | |
te[ 8 ] = d; | |
te[ 1 ] = af + be * d; | |
te[ 5 ] = ae - bf * d; | |
te[ 9 ] = - b * c; | |
te[ 2 ] = bf - ae * d; | |
te[ 6 ] = be + af * d; | |
te[ 10 ] = a * c; | |
} else if ( euler.order === 'YXZ' ) { | |
var ce = c * e, cf = c * f, de = d * e, df = d * f; | |
te[ 0 ] = ce + df * b; | |
te[ 4 ] = de * b - cf; | |
te[ 8 ] = a * d; | |
te[ 1 ] = a * f; | |
te[ 5 ] = a * e; | |
te[ 9 ] = - b; | |
te[ 2 ] = cf * b - de; | |
te[ 6 ] = df + ce * b; | |
te[ 10 ] = a * c; | |
} else if ( euler.order === 'ZXY' ) { | |
var ce = c * e, cf = c * f, de = d * e, df = d * f; | |
te[ 0 ] = ce - df * b; | |
te[ 4 ] = - a * f; | |
te[ 8 ] = de + cf * b; | |
te[ 1 ] = cf + de * b; | |
te[ 5 ] = a * e; | |
te[ 9 ] = df - ce * b; | |
te[ 2 ] = - a * d; | |
te[ 6 ] = b; | |
te[ 10 ] = a * c; | |
} else if ( euler.order === 'ZYX' ) { | |
var ae = a * e, af = a * f, be = b * e, bf = b * f; | |
te[ 0 ] = c * e; | |
te[ 4 ] = be * d - af; | |
te[ 8 ] = ae * d + bf; | |
te[ 1 ] = c * f; | |
te[ 5 ] = bf * d + ae; | |
te[ 9 ] = af * d - be; | |
te[ 2 ] = - d; | |
te[ 6 ] = b * c; | |
te[ 10 ] = a * c; | |
} else if ( euler.order === 'YZX' ) { | |
var ac = a * c, ad = a * d, bc = b * c, bd = b * d; | |
te[ 0 ] = c * e; | |
te[ 4 ] = bd - ac * f; | |
te[ 8 ] = bc * f + ad; | |
te[ 1 ] = f; | |
te[ 5 ] = a * e; | |
te[ 9 ] = - b * e; | |
te[ 2 ] = - d * e; | |
te[ 6 ] = ad * f + bc; | |
te[ 10 ] = ac - bd * f; | |
} else if ( euler.order === 'XZY' ) { | |
var ac = a * c, ad = a * d, bc = b * c, bd = b * d; | |
te[ 0 ] = c * e; | |
te[ 4 ] = - f; | |
te[ 8 ] = d * e; | |
te[ 1 ] = ac * f + bd; | |
te[ 5 ] = a * e; | |
te[ 9 ] = ad * f - bc; | |
te[ 2 ] = bc * f - ad; | |
te[ 6 ] = b * e; | |
te[ 10 ] = bd * f + ac; | |
} | |
// last column | |
te[ 3 ] = 0; | |
te[ 7 ] = 0; | |
te[ 11 ] = 0; | |
// bottom row | |
te[ 12 ] = 0; | |
te[ 13 ] = 0; | |
te[ 14 ] = 0; | |
te[ 15 ] = 1; | |
return this; | |
}, | |
makeRotationFromQuaternion: function ( q ) { | |
var te = this.elements; | |
var x = q.x, y = q.y, z = q.z, w = q.w; | |
var x2 = x + x, y2 = y + y, z2 = z + z; | |
var xx = x * x2, xy = x * y2, xz = x * z2; | |
var yy = y * y2, yz = y * z2, zz = z * z2; | |
var wx = w * x2, wy = w * y2, wz = w * z2; | |
te[ 0 ] = 1 - ( yy + zz ); | |
te[ 4 ] = xy - wz; | |
te[ 8 ] = xz + wy; | |
te[ 1 ] = xy + wz; | |
te[ 5 ] = 1 - ( xx + zz ); | |
te[ 9 ] = yz - wx; | |
te[ 2 ] = xz - wy; | |
te[ 6 ] = yz + wx; | |
te[ 10 ] = 1 - ( xx + yy ); | |
// last column | |
te[ 3 ] = 0; | |
te[ 7 ] = 0; | |
te[ 11 ] = 0; | |
// bottom row | |
te[ 12 ] = 0; | |
te[ 13 ] = 0; | |
te[ 14 ] = 0; | |
te[ 15 ] = 1; | |
return this; | |
}, | |
lookAt: function () { | |
var x, y, z; | |
return function ( eye, target, up ) { | |
if ( x === undefined ) x = new THREE.Vector3(); | |
if ( y === undefined ) y = new THREE.Vector3(); | |
if ( z === undefined ) z = new THREE.Vector3(); | |
var te = this.elements; | |
z.subVectors( eye, target ).normalize(); | |
if ( z.lengthSq() === 0 ) { | |
z.z = 1; | |
} | |
x.crossVectors( up, z ).normalize(); | |
if ( x.lengthSq() === 0 ) { | |
z.x += 0.0001; | |
x.crossVectors( up, z ).normalize(); | |
} | |
y.crossVectors( z, x ); | |
te[ 0 ] = x.x; te[ 4 ] = y.x; te[ 8 ] = z.x; | |
te[ 1 ] = x.y; te[ 5 ] = y.y; te[ 9 ] = z.y; | |
te[ 2 ] = x.z; te[ 6 ] = y.z; te[ 10 ] = z.z; | |
return this; | |
}; | |
}(), | |
multiply: function ( m, n ) { | |
if ( n !== undefined ) { | |
console.warn( 'THREE.Matrix4: .multiply() now only accepts one argument. Use .multiplyMatrices( a, b ) instead.' ); | |
return this.multiplyMatrices( m, n ); | |
} | |
return this.multiplyMatrices( this, m ); | |
}, | |
multiplyMatrices: function ( a, b ) { | |
var ae = a.elements; | |
var be = b.elements; | |
var te = this.elements; | |
var a11 = ae[ 0 ], a12 = ae[ 4 ], a13 = ae[ 8 ], a14 = ae[ 12 ]; | |
var a21 = ae[ 1 ], a22 = ae[ 5 ], a23 = ae[ 9 ], a24 = ae[ 13 ]; | |
var a31 = ae[ 2 ], a32 = ae[ 6 ], a33 = ae[ 10 ], a34 = ae[ 14 ]; | |
var a41 = ae[ 3 ], a42 = ae[ 7 ], a43 = ae[ 11 ], a44 = ae[ 15 ]; | |
var b11 = be[ 0 ], b12 = be[ 4 ], b13 = be[ 8 ], b14 = be[ 12 ]; | |
var b21 = be[ 1 ], b22 = be[ 5 ], b23 = be[ 9 ], b24 = be[ 13 ]; | |
var b31 = be[ 2 ], b32 = be[ 6 ], b33 = be[ 10 ], b34 = be[ 14 ]; | |
var b41 = be[ 3 ], b42 = be[ 7 ], b43 = be[ 11 ], b44 = be[ 15 ]; | |
te[ 0 ] = a11 * b11 + a12 * b21 + a13 * b31 + a14 * b41; | |
te[ 4 ] = a11 * b12 + a12 * b22 + a13 * b32 + a14 * b42; | |
te[ 8 ] = a11 * b13 + a12 * b23 + a13 * b33 + a14 * b43; | |
te[ 12 ] = a11 * b14 + a12 * b24 + a13 * b34 + a14 * b44; | |
te[ 1 ] = a21 * b11 + a22 * b21 + a23 * b31 + a24 * b41; | |
te[ 5 ] = a21 * b12 + a22 * b22 + a23 * b32 + a24 * b42; | |
te[ 9 ] = a21 * b13 + a22 * b23 + a23 * b33 + a24 * b43; | |
te[ 13 ] = a21 * b14 + a22 * b24 + a23 * b34 + a24 * b44; | |
te[ 2 ] = a31 * b11 + a32 * b21 + a33 * b31 + a34 * b41; | |
te[ 6 ] = a31 * b12 + a32 * b22 + a33 * b32 + a34 * b42; | |
te[ 10 ] = a31 * b13 + a32 * b23 + a33 * b33 + a34 * b43; | |
te[ 14 ] = a31 * b14 + a32 * b24 + a33 * b34 + a34 * b44; | |
te[ 3 ] = a41 * b11 + a42 * b21 + a43 * b31 + a44 * b41; | |
te[ 7 ] = a41 * b12 + a42 * b22 + a43 * b32 + a44 * b42; | |
te[ 11 ] = a41 * b13 + a42 * b23 + a43 * b33 + a44 * b43; | |
te[ 15 ] = a41 * b14 + a42 * b24 + a43 * b34 + a44 * b44; | |
return this; | |
}, | |
multiplyToArray: function ( a, b, r ) { | |
var te = this.elements; | |
this.multiplyMatrices( a, b ); | |
r[ 0 ] = te[ 0 ]; r[ 1 ] = te[ 1 ]; r[ 2 ] = te[ 2 ]; r[ 3 ] = te[ 3 ]; | |
r[ 4 ] = te[ 4 ]; r[ 5 ] = te[ 5 ]; r[ 6 ] = te[ 6 ]; r[ 7 ] = te[ 7 ]; | |
r[ 8 ] = te[ 8 ]; r[ 9 ] = te[ 9 ]; r[ 10 ] = te[ 10 ]; r[ 11 ] = te[ 11 ]; | |
r[ 12 ] = te[ 12 ]; r[ 13 ] = te[ 13 ]; r[ 14 ] = te[ 14 ]; r[ 15 ] = te[ 15 ]; | |
return this; | |
}, | |
multiplyScalar: function ( s ) { | |
var te = this.elements; | |
te[ 0 ] *= s; te[ 4 ] *= s; te[ 8 ] *= s; te[ 12 ] *= s; | |
te[ 1 ] *= s; te[ 5 ] *= s; te[ 9 ] *= s; te[ 13 ] *= s; | |
te[ 2 ] *= s; te[ 6 ] *= s; te[ 10 ] *= s; te[ 14 ] *= s; | |
te[ 3 ] *= s; te[ 7 ] *= s; te[ 11 ] *= s; te[ 15 ] *= s; | |
return this; | |
}, | |
applyToVector3Array: function () { | |
var v1; | |
return function ( array, offset, length ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
if ( offset === undefined ) offset = 0; | |
if ( length === undefined ) length = array.length; | |
for ( var i = 0, j = offset; i < length; i += 3, j += 3 ) { | |
v1.fromArray( array, j ); | |
v1.applyMatrix4( this ); | |
v1.toArray( array, j ); | |
} | |
return array; | |
}; | |
}(), | |
applyToBuffer: function () { | |
var v1; | |
return function applyToBuffer( buffer, offset, length ) { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
if ( offset === undefined ) offset = 0; | |
if ( length === undefined ) length = buffer.length / buffer.itemSize; | |
for ( var i = 0, j = offset; i < length; i ++, j ++ ) { | |
v1.x = buffer.getX( j ); | |
v1.y = buffer.getY( j ); | |
v1.z = buffer.getZ( j ); | |
v1.applyMatrix4( this ); | |
buffer.setXYZ( v1.x, v1.y, v1.z ); | |
} | |
return buffer; | |
}; | |
}(), | |
determinant: function () { | |
var te = this.elements; | |
var n11 = te[ 0 ], n12 = te[ 4 ], n13 = te[ 8 ], n14 = te[ 12 ]; | |
var n21 = te[ 1 ], n22 = te[ 5 ], n23 = te[ 9 ], n24 = te[ 13 ]; | |
var n31 = te[ 2 ], n32 = te[ 6 ], n33 = te[ 10 ], n34 = te[ 14 ]; | |
var n41 = te[ 3 ], n42 = te[ 7 ], n43 = te[ 11 ], n44 = te[ 15 ]; | |
//TODO: make this more efficient | |
//( based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm ) | |
return ( | |
n41 * ( | |
+ n14 * n23 * n32 | |
- n13 * n24 * n32 | |
- n14 * n22 * n33 | |
+ n12 * n24 * n33 | |
+ n13 * n22 * n34 | |
- n12 * n23 * n34 | |
) + | |
n42 * ( | |
+ n11 * n23 * n34 | |
- n11 * n24 * n33 | |
+ n14 * n21 * n33 | |
- n13 * n21 * n34 | |
+ n13 * n24 * n31 | |
- n14 * n23 * n31 | |
) + | |
n43 * ( | |
+ n11 * n24 * n32 | |
- n11 * n22 * n34 | |
- n14 * n21 * n32 | |
+ n12 * n21 * n34 | |
+ n14 * n22 * n31 | |
- n12 * n24 * n31 | |
) + | |
n44 * ( | |
- n13 * n22 * n31 | |
- n11 * n23 * n32 | |
+ n11 * n22 * n33 | |
+ n13 * n21 * n32 | |
- n12 * n21 * n33 | |
+ n12 * n23 * n31 | |
) | |
); | |
}, | |
transpose: function () { | |
var te = this.elements; | |
var tmp; | |
tmp = te[ 1 ]; te[ 1 ] = te[ 4 ]; te[ 4 ] = tmp; | |
tmp = te[ 2 ]; te[ 2 ] = te[ 8 ]; te[ 8 ] = tmp; | |
tmp = te[ 6 ]; te[ 6 ] = te[ 9 ]; te[ 9 ] = tmp; | |
tmp = te[ 3 ]; te[ 3 ] = te[ 12 ]; te[ 12 ] = tmp; | |
tmp = te[ 7 ]; te[ 7 ] = te[ 13 ]; te[ 13 ] = tmp; | |
tmp = te[ 11 ]; te[ 11 ] = te[ 14 ]; te[ 14 ] = tmp; | |
return this; | |
}, | |
flattenToArrayOffset: function ( array, offset ) { | |
var te = this.elements; | |
array[ offset ] = te[ 0 ]; | |
array[ offset + 1 ] = te[ 1 ]; | |
array[ offset + 2 ] = te[ 2 ]; | |
array[ offset + 3 ] = te[ 3 ]; | |
array[ offset + 4 ] = te[ 4 ]; | |
array[ offset + 5 ] = te[ 5 ]; | |
array[ offset + 6 ] = te[ 6 ]; | |
array[ offset + 7 ] = te[ 7 ]; | |
array[ offset + 8 ] = te[ 8 ]; | |
array[ offset + 9 ] = te[ 9 ]; | |
array[ offset + 10 ] = te[ 10 ]; | |
array[ offset + 11 ] = te[ 11 ]; | |
array[ offset + 12 ] = te[ 12 ]; | |
array[ offset + 13 ] = te[ 13 ]; | |
array[ offset + 14 ] = te[ 14 ]; | |
array[ offset + 15 ] = te[ 15 ]; | |
return array; | |
}, | |
getPosition: function () { | |
var v1; | |
return function () { | |
if ( v1 === undefined ) v1 = new THREE.Vector3(); | |
console.warn( 'THREE.Matrix4: .getPosition() has been removed. Use Vector3.setFromMatrixPosition( matrix ) instead.' ); | |
var te = this.elements; | |
return v1.set( te[ 12 ], te[ 13 ], te[ 14 ] ); | |
}; | |
}(), | |
setPosition: function ( v ) { | |
var te = this.elements; | |
te[ 12 ] = v.x; | |
te[ 13 ] = v.y; | |
te[ 14 ] = v.z; | |
return this; | |
}, | |
getInverse: function ( m, throwOnInvertible ) { | |
// based on http://www.euclideanspace.com/maths/algebra/matrix/functions/inverse/fourD/index.htm | |
var te = this.elements; | |
var me = m.elements; | |
var n11 = me[ 0 ], n12 = me[ 4 ], n13 = me[ 8 ], n14 = me[ 12 ]; | |
var n21 = me[ 1 ], n22 = me[ 5 ], n23 = me[ 9 ], n24 = me[ 13 ]; | |
var n31 = me[ 2 ], n32 = me[ 6 ], n33 = me[ 10 ], n34 = me[ 14 ]; | |
var n41 = me[ 3 ], n42 = me[ 7 ], n43 = me[ 11 ], n44 = me[ 15 ]; | |
te[ 0 ] = n23 * n34 * n42 - n24 * n33 * n42 + n24 * n32 * n43 - n22 * n34 * n43 - n23 * n32 * n44 + n22 * n33 * n44; | |
te[ 4 ] = n14 * n33 * n42 - n13 * n34 * n42 - n14 * n32 * n43 + n12 * n34 * n43 + n13 * n32 * n44 - n12 * n33 * n44; | |
te[ 8 ] = n13 * n24 * n42 - n14 * n23 * n42 + n14 * n22 * n43 - n12 * n24 * n43 - n13 * n22 * n44 + n12 * n23 * n44; | |
te[ 12 ] = n14 * n23 * n32 - n13 * n24 * n32 - n14 * n22 * n33 + n12 * n24 * n33 + n13 * n22 * n34 - n12 * n23 * n34; | |
te[ 1 ] = n24 * n33 * n41 - n23 * n34 * n41 - n24 * n31 * n43 + n21 * n34 * n43 + n23 * n31 * n44 - n21 * n33 * n44; | |
te[ 5 ] = n13 * n34 * n41 - n14 * n33 * n41 + n14 * n31 * n43 - n11 * n34 * n43 - n13 * n31 * n44 + n11 * n33 * n44; | |
te[ 9 ] = n14 * n23 * n41 - n13 * n24 * n41 - n14 * n21 * n43 + n11 * n24 * n43 + n13 * n21 * n44 - n11 * n23 * n44; | |
te[ 13 ] = n13 * n24 * n31 - n14 * n23 * n31 + n14 * n21 * n33 - n11 * n24 * n33 - n13 * n21 * n34 + n11 * n23 * n34; | |
te[ 2 ] = n22 * n34 * n41 - n24 * n32 * n41 + n24 * n31 * n42 - n21 * n34 * n42 - n22 * n31 * n44 + n21 * n32 * n44; | |
te[ 6 ] = n14 * n32 * n41 - n12 * n34 * n41 - n14 * n31 * n42 + n11 * n34 * n42 + n12 * n31 * n44 - n11 * n32 * n44; | |
te[ 10 ] = n12 * n24 * n41 - n14 * n22 * n41 + n14 * n21 * n42 - n11 * n24 * n42 - n12 * n21 * n44 + n11 * n22 * n44; | |
te[ 14 ] = n14 * n22 * n31 - n12 * n24 * n31 - n14 * n21 * n32 + n11 * n24 * n32 + n12 * n21 * n34 - n11 * n22 * n34; | |
te[ 3 ] = n23 * n32 * n41 - n22 * n33 * n41 - n23 * n31 * n42 + n21 * n33 * n42 + n22 * n31 * n43 - n21 * n32 * n43; | |
te[ 7 ] = n12 * n33 * n41 - n13 * n32 * n41 + n13 * n31 * n42 - n11 * n33 * n42 - n12 * n31 * n43 + n11 * n32 * n43; | |
te[ 11 ] = n13 * n22 * n41 - n12 * n23 * n41 - n13 * n21 * n42 + n11 * n23 * n42 + n12 * n21 * n43 - n11 * n22 * n43; | |
te[ 15 ] = n12 * n23 * n31 - n13 * n22 * n31 + n13 * n21 * n32 - n11 * n23 * n32 - n12 * n21 * n33 + n11 * n22 * n33; | |
var det = n11 * te[ 0 ] + n21 * te[ 4 ] + n31 * te[ 8 ] + n41 * te[ 12 ]; | |
if ( det === 0 ) { | |
var msg = "THREE.Matrix4.getInverse(): can't invert matrix, determinant is 0"; | |
if ( throwOnInvertible || false ) { | |
throw new Error( msg ); | |
} else { | |
console.warn( msg ); | |
} | |
this.identity(); | |
return this; | |
} | |
this.multiplyScalar( 1 / det ); | |
return this; | |
}, | |
scale: function ( v ) { | |
var te = this.elements; | |
var x = v.x, y = v.y, z = v.z; | |
te[ 0 ] *= x; te[ 4 ] *= y; te[ 8 ] *= z; | |
te[ 1 ] *= x; te[ 5 ] *= y; te[ 9 ] *= z; | |
te[ 2 ] *= x; te[ 6 ] *= y; te[ 10 ] *= z; | |
te[ 3 ] *= x; te[ 7 ] *= y; te[ 11 ] *= z; | |
return this; | |
}, | |
getMaxScaleOnAxis: function () { | |
var te = this.elements; | |
var scaleXSq = te[ 0 ] * te[ 0 ] + te[ 1 ] * te[ 1 ] + te[ 2 ] * te[ 2 ]; | |
var scaleYSq = te[ 4 ] * te[ 4 ] + te[ 5 ] * te[ 5 ] + te[ 6 ] * te[ 6 ]; | |
var scaleZSq = te[ 8 ] * te[ 8 ] + te[ 9 ] * te[ 9 ] + te[ 10 ] * te[ 10 ]; | |
return Math.sqrt( Math.max( scaleXSq, scaleYSq, scaleZSq ) ); | |
}, | |
makeTranslation: function ( x, y, z ) { | |
this.set( | |
1, 0, 0, x, | |
0, 1, 0, y, | |
0, 0, 1, z, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
makeRotationX: function ( theta ) { | |
var c = Math.cos( theta ), s = Math.sin( theta ); | |
this.set( | |
1, 0, 0, 0, | |
0, c, - s, 0, | |
0, s, c, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
makeRotationY: function ( theta ) { | |
var c = Math.cos( theta ), s = Math.sin( theta ); | |
this.set( | |
c, 0, s, 0, | |
0, 1, 0, 0, | |
- s, 0, c, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
makeRotationZ: function ( theta ) { | |
var c = Math.cos( theta ), s = Math.sin( theta ); | |
this.set( | |
c, - s, 0, 0, | |
s, c, 0, 0, | |
0, 0, 1, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
makeRotationAxis: function ( axis, angle ) { | |
// Based on http://www.gamedev.net/reference/articles/article1199.asp | |
var c = Math.cos( angle ); | |
var s = Math.sin( angle ); | |
var t = 1 - c; | |
var x = axis.x, y = axis.y, z = axis.z; | |
var tx = t * x, ty = t * y; | |
this.set( | |
tx * x + c, tx * y - s * z, tx * z + s * y, 0, | |
tx * y + s * z, ty * y + c, ty * z - s * x, 0, | |
tx * z - s * y, ty * z + s * x, t * z * z + c, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
makeScale: function ( x, y, z ) { | |
this.set( | |
x, 0, 0, 0, | |
0, y, 0, 0, | |
0, 0, z, 0, | |
0, 0, 0, 1 | |
); | |
return this; | |
}, | |
compose: function ( position, quaternion, scale ) { | |
this.makeRotationFromQuaternion( quaternion ); | |
this.scale( scale ); | |
this.setPosition( position ); | |
return this; | |
}, | |
decompose: function () { | |
var vector, matrix; | |
return function ( position, quaternion, scale ) { | |
if ( vector === undefined ) vector = new THREE.Vector3(); | |
if ( matrix === undefined ) matrix = new THREE.Matrix4(); | |
var te = this.elements; | |
var sx = vector.set( te[ 0 ], te[ 1 ], te[ 2 ] ).length(); | |
var sy = vector.set( te[ 4 ], te[ 5 ], te[ 6 ] ).length(); | |
var sz = vector.set( te[ 8 ], te[ 9 ], te[ 10 ] ).length(); | |
// if determine is negative, we need to invert one scale | |
var det = this.determinant(); | |
if ( det < 0 ) { | |
sx = - sx; | |
} | |
position.x = te[ 12 ]; | |
position.y = te[ 13 ]; | |
position.z = te[ 14 ]; | |
// scale the rotation part | |
matrix.elements.set( this.elements ); // at this point matrix is incomplete so we can't use .copy() | |
var invSX = 1 / sx; | |
var invSY = 1 / sy; | |
var invSZ = 1 / sz; | |
matrix.elements[ 0 ] *= invSX; | |
matrix.elements[ 1 ] *= invSX; | |
matrix.elements[ 2 ] *= invSX; | |
matrix.elements[ 4 ] *= invSY; | |
matrix.elements[ 5 ] *= invSY; | |
matrix.elements[ 6 ] *= invSY; | |
matrix.elements[ 8 ] *= invSZ; | |
matrix.elements[ 9 ] *= invSZ; | |
matrix.elements[ 10 ] *= invSZ; | |
quaternion.setFromRotationMatrix( matrix ); | |
scale.x = sx; | |
scale.y = sy; | |
scale.z = sz; | |
return this; | |
}; | |
}(), | |
makeFrustum: function ( left, right, bottom, top, near, far ) { | |
var te = this.elements; | |
var x = 2 * near / ( right - left ); | |
var y = 2 * near / ( top - bottom ); | |
var a = ( right + left ) / ( right - left ); | |
var b = ( top + bottom ) / ( top - bottom ); | |
var c = - ( far + near ) / ( far - near ); | |
var d = - 2 * far * near / ( far - near ); | |
te[ 0 ] = x; te[ 4 ] = 0; te[ 8 ] = a; te[ 12 ] = 0; | |
te[ 1 ] = 0; te[ 5 ] = y; te[ 9 ] = b; te[ 13 ] = 0; | |
te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = c; te[ 14 ] = d; | |
te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = - 1; te[ 15 ] = 0; | |
return this; | |
}, | |
makePerspective: function ( fov, aspect, near, far ) { | |
var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); | |
var ymin = - ymax; | |
var xmin = ymin * aspect; | |
var xmax = ymax * aspect; | |
return this.makeFrustum( xmin, xmax, ymin, ymax, near, far ); | |
}, | |
makeOrthographic: function ( left, right, top, bottom, near, far ) { | |
var te = this.elements; | |
var w = right - left; | |
var h = top - bottom; | |
var p = far - near; | |
var x = ( right + left ) / w; | |
var y = ( top + bottom ) / h; | |
var z = ( far + near ) / p; | |
te[ 0 ] = 2 / w; te[ 4 ] = 0; te[ 8 ] = 0; te[ 12 ] = - x; | |
te[ 1 ] = 0; te[ 5 ] = 2 / h; te[ 9 ] = 0; te[ 13 ] = - y; | |
te[ 2 ] = 0; te[ 6 ] = 0; te[ 10 ] = - 2 / p; te[ 14 ] = - z; | |
te[ 3 ] = 0; te[ 7 ] = 0; te[ 11 ] = 0; te[ 15 ] = 1; | |
return this; | |
}, | |
equals: function ( matrix ) { | |
var te = this.elements; | |
var me = matrix.elements; | |
for ( var i = 0; i < 16; i ++ ) { | |
if ( te[ i ] !== me[ i ] ) return false; | |
} | |
return true; | |
}, | |
fromArray: function ( array ) { | |
this.elements.set( array ); | |
return this; | |
}, | |
toArray: function () { | |
var te = this.elements; | |
return [ | |
te[ 0 ], te[ 1 ], te[ 2 ], te[ 3 ], | |
te[ 4 ], te[ 5 ], te[ 6 ], te[ 7 ], | |
te[ 8 ], te[ 9 ], te[ 10 ], te[ 11 ], | |
te[ 12 ], te[ 13 ], te[ 14 ], te[ 15 ] | |
]; | |
} | |
}; | |
// File:src/math/Ray.js | |
/** | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Ray = function ( origin, direction ) { | |
this.origin = ( origin !== undefined ) ? origin : new THREE.Vector3(); | |
this.direction = ( direction !== undefined ) ? direction : new THREE.Vector3(); | |
}; | |
THREE.Ray.prototype = { | |
constructor: THREE.Ray, | |
set: function ( origin, direction ) { | |
this.origin.copy( origin ); | |
this.direction.copy( direction ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( ray ) { | |
this.origin.copy( ray.origin ); | |
this.direction.copy( ray.direction ); | |
return this; | |
}, | |
at: function ( t, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.copy( this.direction ).multiplyScalar( t ).add( this.origin ); | |
}, | |
lookAt: function ( v ) { | |
this.direction.copy( v ).sub( this.origin ).normalize(); | |
}, | |
recast: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( t ) { | |
this.origin.copy( this.at( t, v1 ) ); | |
return this; | |
}; | |
}(), | |
closestPointToPoint: function ( point, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
result.subVectors( point, this.origin ); | |
var directionDistance = result.dot( this.direction ); | |
if ( directionDistance < 0 ) { | |
return result.copy( this.origin ); | |
} | |
return result.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); | |
}, | |
distanceToPoint: function ( point ) { | |
return Math.sqrt( this.distanceSqToPoint( point ) ); | |
}, | |
distanceSqToPoint: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( point ) { | |
var directionDistance = v1.subVectors( point, this.origin ).dot( this.direction ); | |
// point behind the ray | |
if ( directionDistance < 0 ) { | |
return this.origin.distanceToSquared( point ); | |
} | |
v1.copy( this.direction ).multiplyScalar( directionDistance ).add( this.origin ); | |
return v1.distanceToSquared( point ); | |
}; | |
}(), | |
distanceSqToSegment: function () { | |
var segCenter = new THREE.Vector3(); | |
var segDir = new THREE.Vector3(); | |
var diff = new THREE.Vector3(); | |
return function ( v0, v1, optionalPointOnRay, optionalPointOnSegment ) { | |
// from http://www.geometrictools.com/LibMathematics/Distance/Wm5DistRay3Segment3.cpp | |
// It returns the min distance between the ray and the segment | |
// defined by v0 and v1 | |
// It can also set two optional targets : | |
// - The closest point on the ray | |
// - The closest point on the segment | |
segCenter.copy( v0 ).add( v1 ).multiplyScalar( 0.5 ); | |
segDir.copy( v1 ).sub( v0 ).normalize(); | |
diff.copy( this.origin ).sub( segCenter ); | |
var segExtent = v0.distanceTo( v1 ) * 0.5; | |
var a01 = - this.direction.dot( segDir ); | |
var b0 = diff.dot( this.direction ); | |
var b1 = - diff.dot( segDir ); | |
var c = diff.lengthSq(); | |
var det = Math.abs( 1 - a01 * a01 ); | |
var s0, s1, sqrDist, extDet; | |
if ( det > 0 ) { | |
// The ray and segment are not parallel. | |
s0 = a01 * b1 - b0; | |
s1 = a01 * b0 - b1; | |
extDet = segExtent * det; | |
if ( s0 >= 0 ) { | |
if ( s1 >= - extDet ) { | |
if ( s1 <= extDet ) { | |
// region 0 | |
// Minimum at interior points of ray and segment. | |
var invDet = 1 / det; | |
s0 *= invDet; | |
s1 *= invDet; | |
sqrDist = s0 * ( s0 + a01 * s1 + 2 * b0 ) + s1 * ( a01 * s0 + s1 + 2 * b1 ) + c; | |
} else { | |
// region 1 | |
s1 = segExtent; | |
s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
} | |
} else { | |
// region 5 | |
s1 = - segExtent; | |
s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
} | |
} else { | |
if ( s1 <= - extDet ) { | |
// region 4 | |
s0 = Math.max( 0, - ( - a01 * segExtent + b0 ) ); | |
s1 = ( s0 > 0 ) ? - segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
} else if ( s1 <= extDet ) { | |
// region 3 | |
s0 = 0; | |
s1 = Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
sqrDist = s1 * ( s1 + 2 * b1 ) + c; | |
} else { | |
// region 2 | |
s0 = Math.max( 0, - ( a01 * segExtent + b0 ) ); | |
s1 = ( s0 > 0 ) ? segExtent : Math.min( Math.max( - segExtent, - b1 ), segExtent ); | |
sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
} | |
} | |
} else { | |
// Ray and segment are parallel. | |
s1 = ( a01 > 0 ) ? - segExtent : segExtent; | |
s0 = Math.max( 0, - ( a01 * s1 + b0 ) ); | |
sqrDist = - s0 * s0 + s1 * ( s1 + 2 * b1 ) + c; | |
} | |
if ( optionalPointOnRay ) { | |
optionalPointOnRay.copy( this.direction ).multiplyScalar( s0 ).add( this.origin ); | |
} | |
if ( optionalPointOnSegment ) { | |
optionalPointOnSegment.copy( segDir ).multiplyScalar( s1 ).add( segCenter ); | |
} | |
return sqrDist; | |
}; | |
}(), | |
intersectSphere: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( sphere, optionalTarget ) { | |
v1.subVectors( sphere.center, this.origin ); | |
var tca = v1.dot( this.direction ); | |
var d2 = v1.dot( v1 ) - tca * tca; | |
var radius2 = sphere.radius * sphere.radius; | |
if ( d2 > radius2 ) return null; | |
var thc = Math.sqrt( radius2 - d2 ); | |
// t0 = first intersect point - entrance on front of sphere | |
var t0 = tca - thc; | |
// t1 = second intersect point - exit point on back of sphere | |
var t1 = tca + thc; | |
// test to see if both t0 and t1 are behind the ray - if so, return null | |
if ( t0 < 0 && t1 < 0 ) return null; | |
// test to see if t0 is behind the ray: | |
// if it is, the ray is inside the sphere, so return the second exit point scaled by t1, | |
// in order to always return an intersect point that is in front of the ray. | |
if ( t0 < 0 ) return this.at( t1, optionalTarget ); | |
// else t0 is in front of the ray, so return the first collision point scaled by t0 | |
return this.at( t0, optionalTarget ); | |
} | |
}(), | |
intersectsSphere: function ( sphere ) { | |
return this.distanceToPoint( sphere.center ) <= sphere.radius; | |
}, | |
distanceToPlane: function ( plane ) { | |
var denominator = plane.normal.dot( this.direction ); | |
if ( denominator === 0 ) { | |
// line is coplanar, return origin | |
if ( plane.distanceToPoint( this.origin ) === 0 ) { | |
return 0; | |
} | |
// Null is preferable to undefined since undefined means.... it is undefined | |
return null; | |
} | |
var t = - ( this.origin.dot( plane.normal ) + plane.constant ) / denominator; | |
// Return if the ray never intersects the plane | |
return t >= 0 ? t : null; | |
}, | |
intersectPlane: function ( plane, optionalTarget ) { | |
var t = this.distanceToPlane( plane ); | |
if ( t === null ) { | |
return null; | |
} | |
return this.at( t, optionalTarget ); | |
}, | |
intersectsPlane: function ( plane ) { | |
// check if the ray lies on the plane first | |
var distToPoint = plane.distanceToPoint( this.origin ); | |
if ( distToPoint === 0 ) { | |
return true; | |
} | |
var denominator = plane.normal.dot( this.direction ); | |
if ( denominator * distToPoint < 0 ) { | |
return true; | |
} | |
// ray origin is behind the plane (and is pointing behind it) | |
return false; | |
}, | |
intersectBox: function ( box, optionalTarget ) { | |
var tmin, tmax, tymin, tymax, tzmin, tzmax; | |
var invdirx = 1 / this.direction.x, | |
invdiry = 1 / this.direction.y, | |
invdirz = 1 / this.direction.z; | |
var origin = this.origin; | |
if ( invdirx >= 0 ) { | |
tmin = ( box.min.x - origin.x ) * invdirx; | |
tmax = ( box.max.x - origin.x ) * invdirx; | |
} else { | |
tmin = ( box.max.x - origin.x ) * invdirx; | |
tmax = ( box.min.x - origin.x ) * invdirx; | |
} | |
if ( invdiry >= 0 ) { | |
tymin = ( box.min.y - origin.y ) * invdiry; | |
tymax = ( box.max.y - origin.y ) * invdiry; | |
} else { | |
tymin = ( box.max.y - origin.y ) * invdiry; | |
tymax = ( box.min.y - origin.y ) * invdiry; | |
} | |
if ( ( tmin > tymax ) || ( tymin > tmax ) ) return null; | |
// These lines also handle the case where tmin or tmax is NaN | |
// (result of 0 * Infinity). x !== x returns true if x is NaN | |
if ( tymin > tmin || tmin !== tmin ) tmin = tymin; | |
if ( tymax < tmax || tmax !== tmax ) tmax = tymax; | |
if ( invdirz >= 0 ) { | |
tzmin = ( box.min.z - origin.z ) * invdirz; | |
tzmax = ( box.max.z - origin.z ) * invdirz; | |
} else { | |
tzmin = ( box.max.z - origin.z ) * invdirz; | |
tzmax = ( box.min.z - origin.z ) * invdirz; | |
} | |
if ( ( tmin > tzmax ) || ( tzmin > tmax ) ) return null; | |
if ( tzmin > tmin || tmin !== tmin ) tmin = tzmin; | |
if ( tzmax < tmax || tmax !== tmax ) tmax = tzmax; | |
//return point closest to the ray (positive side) | |
if ( tmax < 0 ) return null; | |
return this.at( tmin >= 0 ? tmin : tmax, optionalTarget ); | |
}, | |
intersectsBox: ( function () { | |
var v = new THREE.Vector3(); | |
return function ( box ) { | |
return this.intersectBox( box, v ) !== null; | |
}; | |
} )(), | |
intersectTriangle: function () { | |
// Compute the offset origin, edges, and normal. | |
var diff = new THREE.Vector3(); | |
var edge1 = new THREE.Vector3(); | |
var edge2 = new THREE.Vector3(); | |
var normal = new THREE.Vector3(); | |
return function ( a, b, c, backfaceCulling, optionalTarget ) { | |
// from http://www.geometrictools.com/LibMathematics/Intersection/Wm5IntrRay3Triangle3.cpp | |
edge1.subVectors( b, a ); | |
edge2.subVectors( c, a ); | |
normal.crossVectors( edge1, edge2 ); | |
// Solve Q + t*D = b1*E1 + b2*E2 (Q = kDiff, D = ray direction, | |
// E1 = kEdge1, E2 = kEdge2, N = Cross(E1,E2)) by | |
// |Dot(D,N)|*b1 = sign(Dot(D,N))*Dot(D,Cross(Q,E2)) | |
// |Dot(D,N)|*b2 = sign(Dot(D,N))*Dot(D,Cross(E1,Q)) | |
// |Dot(D,N)|*t = -sign(Dot(D,N))*Dot(Q,N) | |
var DdN = this.direction.dot( normal ); | |
var sign; | |
if ( DdN > 0 ) { | |
if ( backfaceCulling ) return null; | |
sign = 1; | |
} else if ( DdN < 0 ) { | |
sign = - 1; | |
DdN = - DdN; | |
} else { | |
return null; | |
} | |
diff.subVectors( this.origin, a ); | |
var DdQxE2 = sign * this.direction.dot( edge2.crossVectors( diff, edge2 ) ); | |
// b1 < 0, no intersection | |
if ( DdQxE2 < 0 ) { | |
return null; | |
} | |
var DdE1xQ = sign * this.direction.dot( edge1.cross( diff ) ); | |
// b2 < 0, no intersection | |
if ( DdE1xQ < 0 ) { | |
return null; | |
} | |
// b1+b2 > 1, no intersection | |
if ( DdQxE2 + DdE1xQ > DdN ) { | |
return null; | |
} | |
// Line intersects triangle, check if ray does. | |
var QdN = - sign * diff.dot( normal ); | |
// t < 0, no intersection | |
if ( QdN < 0 ) { | |
return null; | |
} | |
// Ray intersects triangle. | |
return this.at( QdN / DdN, optionalTarget ); | |
}; | |
}(), | |
applyMatrix4: function ( matrix4 ) { | |
this.direction.add( this.origin ).applyMatrix4( matrix4 ); | |
this.origin.applyMatrix4( matrix4 ); | |
this.direction.sub( this.origin ); | |
this.direction.normalize(); | |
return this; | |
}, | |
equals: function ( ray ) { | |
return ray.origin.equals( this.origin ) && ray.direction.equals( this.direction ); | |
} | |
}; | |
// File:src/math/Sphere.js | |
/** | |
* @author bhouston / http://clara.io | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Sphere = function ( center, radius ) { | |
this.center = ( center !== undefined ) ? center : new THREE.Vector3(); | |
this.radius = ( radius !== undefined ) ? radius : 0; | |
}; | |
THREE.Sphere.prototype = { | |
constructor: THREE.Sphere, | |
set: function ( center, radius ) { | |
this.center.copy( center ); | |
this.radius = radius; | |
return this; | |
}, | |
setFromPoints: function () { | |
var box = new THREE.Box3(); | |
return function ( points, optionalCenter ) { | |
var center = this.center; | |
if ( optionalCenter !== undefined ) { | |
center.copy( optionalCenter ); | |
} else { | |
box.setFromPoints( points ).center( center ); | |
} | |
var maxRadiusSq = 0; | |
for ( var i = 0, il = points.length; i < il; i ++ ) { | |
maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( points[ i ] ) ); | |
} | |
this.radius = Math.sqrt( maxRadiusSq ); | |
return this; | |
}; | |
}(), | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( sphere ) { | |
this.center.copy( sphere.center ); | |
this.radius = sphere.radius; | |
return this; | |
}, | |
empty: function () { | |
return ( this.radius <= 0 ); | |
}, | |
containsPoint: function ( point ) { | |
return ( point.distanceToSquared( this.center ) <= ( this.radius * this.radius ) ); | |
}, | |
distanceToPoint: function ( point ) { | |
return ( point.distanceTo( this.center ) - this.radius ); | |
}, | |
intersectsSphere: function ( sphere ) { | |
var radiusSum = this.radius + sphere.radius; | |
return sphere.center.distanceToSquared( this.center ) <= ( radiusSum * radiusSum ); | |
}, | |
intersectsBox: function ( box ) { | |
return box.intersectsSphere( this ); | |
}, | |
intersectsPlane: function ( plane ) { | |
// We use the following equation to compute the signed distance from | |
// the center of the sphere to the plane. | |
// | |
// distance = q * n - d | |
// | |
// If this distance is greater than the radius of the sphere, | |
// then there is no intersection. | |
return Math.abs( this.center.dot( plane.normal ) - plane.constant ) <= this.radius; | |
}, | |
clampPoint: function ( point, optionalTarget ) { | |
var deltaLengthSq = this.center.distanceToSquared( point ); | |
var result = optionalTarget || new THREE.Vector3(); | |
result.copy( point ); | |
if ( deltaLengthSq > ( this.radius * this.radius ) ) { | |
result.sub( this.center ).normalize(); | |
result.multiplyScalar( this.radius ).add( this.center ); | |
} | |
return result; | |
}, | |
getBoundingBox: function ( optionalTarget ) { | |
var box = optionalTarget || new THREE.Box3(); | |
box.set( this.center, this.center ); | |
box.expandByScalar( this.radius ); | |
return box; | |
}, | |
applyMatrix4: function ( matrix ) { | |
this.center.applyMatrix4( matrix ); | |
this.radius = this.radius * matrix.getMaxScaleOnAxis(); | |
return this; | |
}, | |
translate: function ( offset ) { | |
this.center.add( offset ); | |
return this; | |
}, | |
equals: function ( sphere ) { | |
return sphere.center.equals( this.center ) && ( sphere.radius === this.radius ); | |
} | |
}; | |
// File:src/math/Frustum.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Frustum = function ( p0, p1, p2, p3, p4, p5 ) { | |
this.planes = [ | |
( p0 !== undefined ) ? p0 : new THREE.Plane(), | |
( p1 !== undefined ) ? p1 : new THREE.Plane(), | |
( p2 !== undefined ) ? p2 : new THREE.Plane(), | |
( p3 !== undefined ) ? p3 : new THREE.Plane(), | |
( p4 !== undefined ) ? p4 : new THREE.Plane(), | |
( p5 !== undefined ) ? p5 : new THREE.Plane() | |
]; | |
}; | |
THREE.Frustum.prototype = { | |
constructor: THREE.Frustum, | |
set: function ( p0, p1, p2, p3, p4, p5 ) { | |
var planes = this.planes; | |
planes[ 0 ].copy( p0 ); | |
planes[ 1 ].copy( p1 ); | |
planes[ 2 ].copy( p2 ); | |
planes[ 3 ].copy( p3 ); | |
planes[ 4 ].copy( p4 ); | |
planes[ 5 ].copy( p5 ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( frustum ) { | |
var planes = this.planes; | |
for ( var i = 0; i < 6; i ++ ) { | |
planes[ i ].copy( frustum.planes[ i ] ); | |
} | |
return this; | |
}, | |
setFromMatrix: function ( m ) { | |
var planes = this.planes; | |
var me = m.elements; | |
var me0 = me[ 0 ], me1 = me[ 1 ], me2 = me[ 2 ], me3 = me[ 3 ]; | |
var me4 = me[ 4 ], me5 = me[ 5 ], me6 = me[ 6 ], me7 = me[ 7 ]; | |
var me8 = me[ 8 ], me9 = me[ 9 ], me10 = me[ 10 ], me11 = me[ 11 ]; | |
var me12 = me[ 12 ], me13 = me[ 13 ], me14 = me[ 14 ], me15 = me[ 15 ]; | |
planes[ 0 ].setComponents( me3 - me0, me7 - me4, me11 - me8, me15 - me12 ).normalize(); | |
planes[ 1 ].setComponents( me3 + me0, me7 + me4, me11 + me8, me15 + me12 ).normalize(); | |
planes[ 2 ].setComponents( me3 + me1, me7 + me5, me11 + me9, me15 + me13 ).normalize(); | |
planes[ 3 ].setComponents( me3 - me1, me7 - me5, me11 - me9, me15 - me13 ).normalize(); | |
planes[ 4 ].setComponents( me3 - me2, me7 - me6, me11 - me10, me15 - me14 ).normalize(); | |
planes[ 5 ].setComponents( me3 + me2, me7 + me6, me11 + me10, me15 + me14 ).normalize(); | |
return this; | |
}, | |
intersectsObject: function () { | |
var sphere = new THREE.Sphere(); | |
return function ( object ) { | |
var geometry = object.geometry; | |
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
sphere.copy( geometry.boundingSphere ); | |
sphere.applyMatrix4( object.matrixWorld ); | |
return this.intersectsSphere( sphere ); | |
}; | |
}(), | |
intersectsSphere: function ( sphere ) { | |
var planes = this.planes; | |
var center = sphere.center; | |
var negRadius = - sphere.radius; | |
for ( var i = 0; i < 6; i ++ ) { | |
var distance = planes[ i ].distanceToPoint( center ); | |
if ( distance < negRadius ) { | |
return false; | |
} | |
} | |
return true; | |
}, | |
intersectsBox: function () { | |
var p1 = new THREE.Vector3(), | |
p2 = new THREE.Vector3(); | |
return function ( box ) { | |
var planes = this.planes; | |
for ( var i = 0; i < 6 ; i ++ ) { | |
var plane = planes[ i ]; | |
p1.x = plane.normal.x > 0 ? box.min.x : box.max.x; | |
p2.x = plane.normal.x > 0 ? box.max.x : box.min.x; | |
p1.y = plane.normal.y > 0 ? box.min.y : box.max.y; | |
p2.y = plane.normal.y > 0 ? box.max.y : box.min.y; | |
p1.z = plane.normal.z > 0 ? box.min.z : box.max.z; | |
p2.z = plane.normal.z > 0 ? box.max.z : box.min.z; | |
var d1 = plane.distanceToPoint( p1 ); | |
var d2 = plane.distanceToPoint( p2 ); | |
// if both outside plane, no intersection | |
if ( d1 < 0 && d2 < 0 ) { | |
return false; | |
} | |
} | |
return true; | |
}; | |
}(), | |
containsPoint: function ( point ) { | |
var planes = this.planes; | |
for ( var i = 0; i < 6; i ++ ) { | |
if ( planes[ i ].distanceToPoint( point ) < 0 ) { | |
return false; | |
} | |
} | |
return true; | |
} | |
}; | |
// File:src/math/Plane.js | |
/** | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Plane = function ( normal, constant ) { | |
this.normal = ( normal !== undefined ) ? normal : new THREE.Vector3( 1, 0, 0 ); | |
this.constant = ( constant !== undefined ) ? constant : 0; | |
}; | |
THREE.Plane.prototype = { | |
constructor: THREE.Plane, | |
set: function ( normal, constant ) { | |
this.normal.copy( normal ); | |
this.constant = constant; | |
return this; | |
}, | |
setComponents: function ( x, y, z, w ) { | |
this.normal.set( x, y, z ); | |
this.constant = w; | |
return this; | |
}, | |
setFromNormalAndCoplanarPoint: function ( normal, point ) { | |
this.normal.copy( normal ); | |
this.constant = - point.dot( this.normal ); // must be this.normal, not normal, as this.normal is normalized | |
return this; | |
}, | |
setFromCoplanarPoints: function () { | |
var v1 = new THREE.Vector3(); | |
var v2 = new THREE.Vector3(); | |
return function ( a, b, c ) { | |
var normal = v1.subVectors( c, b ).cross( v2.subVectors( a, b ) ).normalize(); | |
// Q: should an error be thrown if normal is zero (e.g. degenerate plane)? | |
this.setFromNormalAndCoplanarPoint( normal, a ); | |
return this; | |
}; | |
}(), | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( plane ) { | |
this.normal.copy( plane.normal ); | |
this.constant = plane.constant; | |
return this; | |
}, | |
normalize: function () { | |
// Note: will lead to a divide by zero if the plane is invalid. | |
var inverseNormalLength = 1.0 / this.normal.length(); | |
this.normal.multiplyScalar( inverseNormalLength ); | |
this.constant *= inverseNormalLength; | |
return this; | |
}, | |
negate: function () { | |
this.constant *= - 1; | |
this.normal.negate(); | |
return this; | |
}, | |
distanceToPoint: function ( point ) { | |
return this.normal.dot( point ) + this.constant; | |
}, | |
distanceToSphere: function ( sphere ) { | |
return this.distanceToPoint( sphere.center ) - sphere.radius; | |
}, | |
projectPoint: function ( point, optionalTarget ) { | |
return this.orthoPoint( point, optionalTarget ).sub( point ).negate(); | |
}, | |
orthoPoint: function ( point, optionalTarget ) { | |
var perpendicularMagnitude = this.distanceToPoint( point ); | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.copy( this.normal ).multiplyScalar( perpendicularMagnitude ); | |
}, | |
intersectLine: function () { | |
var v1 = new THREE.Vector3(); | |
return function ( line, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
var direction = line.delta( v1 ); | |
var denominator = this.normal.dot( direction ); | |
if ( denominator === 0 ) { | |
// line is coplanar, return origin | |
if ( this.distanceToPoint( line.start ) === 0 ) { | |
return result.copy( line.start ); | |
} | |
// Unsure if this is the correct method to handle this case. | |
return undefined; | |
} | |
var t = - ( line.start.dot( this.normal ) + this.constant ) / denominator; | |
if ( t < 0 || t > 1 ) { | |
return undefined; | |
} | |
return result.copy( direction ).multiplyScalar( t ).add( line.start ); | |
}; | |
}(), | |
intersectsLine: function ( line ) { | |
// Note: this tests if a line intersects the plane, not whether it (or its end-points) are coplanar with it. | |
var startSign = this.distanceToPoint( line.start ); | |
var endSign = this.distanceToPoint( line.end ); | |
return ( startSign < 0 && endSign > 0 ) || ( endSign < 0 && startSign > 0 ); | |
}, | |
intersectsBox: function ( box ) { | |
return box.intersectsPlane( this ); | |
}, | |
intersectsSphere: function ( sphere ) { | |
return sphere.intersectsPlane( this ); | |
}, | |
coplanarPoint: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.copy( this.normal ).multiplyScalar( - this.constant ); | |
}, | |
applyMatrix4: function () { | |
var v1 = new THREE.Vector3(); | |
var v2 = new THREE.Vector3(); | |
var m1 = new THREE.Matrix3(); | |
return function ( matrix, optionalNormalMatrix ) { | |
// compute new normal based on theory here: | |
// http://www.songho.ca/opengl/gl_normaltransform.html | |
var normalMatrix = optionalNormalMatrix || m1.getNormalMatrix( matrix ); | |
var newNormal = v1.copy( this.normal ).applyMatrix3( normalMatrix ); | |
var newCoplanarPoint = this.coplanarPoint( v2 ); | |
newCoplanarPoint.applyMatrix4( matrix ); | |
this.setFromNormalAndCoplanarPoint( newNormal, newCoplanarPoint ); | |
return this; | |
}; | |
}(), | |
translate: function ( offset ) { | |
this.constant = this.constant - offset.dot( this.normal ); | |
return this; | |
}, | |
equals: function ( plane ) { | |
return plane.normal.equals( this.normal ) && ( plane.constant === this.constant ); | |
} | |
}; | |
// File:src/math/Math.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Math = { | |
generateUUID: function () { | |
// http://www.broofa.com/Tools/Math.uuid.htm | |
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' ); | |
var uuid = new Array( 36 ); | |
var rnd = 0, r; | |
return function () { | |
for ( var i = 0; i < 36; i ++ ) { | |
if ( i === 8 || i === 13 || i === 18 || i === 23 ) { | |
uuid[ i ] = '-'; | |
} else if ( i === 14 ) { | |
uuid[ i ] = '4'; | |
} else { | |
if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0; | |
r = rnd & 0xf; | |
rnd = rnd >> 4; | |
uuid[ i ] = chars[ ( i === 19 ) ? ( r & 0x3 ) | 0x8 : r ]; | |
} | |
} | |
return uuid.join( '' ); | |
}; | |
}(), | |
clamp: function ( value, min, max ) { | |
return Math.max( min, Math.min( max, value ) ); | |
}, | |
// compute euclidian modulo of m % n | |
// https://en.wikipedia.org/wiki/Modulo_operation | |
euclideanModulo: function ( n, m ) { | |
return ( ( n % m ) + m ) % m; | |
}, | |
// Linear mapping from range <a1, a2> to range <b1, b2> | |
mapLinear: function ( x, a1, a2, b1, b2 ) { | |
return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 ); | |
}, | |
// http://en.wikipedia.org/wiki/Smoothstep | |
smoothstep: function ( x, min, max ) { | |
if ( x <= min ) return 0; | |
if ( x >= max ) return 1; | |
x = ( x - min ) / ( max - min ); | |
return x * x * ( 3 - 2 * x ); | |
}, | |
smootherstep: function ( x, min, max ) { | |
if ( x <= min ) return 0; | |
if ( x >= max ) return 1; | |
x = ( x - min ) / ( max - min ); | |
return x * x * x * ( x * ( x * 6 - 15 ) + 10 ); | |
}, | |
random16: function () { | |
console.warn( 'THREE.Math.random16() has been deprecated. Use Math.random() instead.' ); | |
return Math.random(); | |
}, | |
// Random integer from <low, high> interval | |
randInt: function ( low, high ) { | |
return low + Math.floor( Math.random() * ( high - low + 1 ) ); | |
}, | |
// Random float from <low, high> interval | |
randFloat: function ( low, high ) { | |
return low + Math.random() * ( high - low ); | |
}, | |
// Random float from <-range/2, range/2> interval | |
randFloatSpread: function ( range ) { | |
return range * ( 0.5 - Math.random() ); | |
}, | |
degToRad: function () { | |
var degreeToRadiansFactor = Math.PI / 180; | |
return function ( degrees ) { | |
return degrees * degreeToRadiansFactor; | |
}; | |
}(), | |
radToDeg: function () { | |
var radianToDegreesFactor = 180 / Math.PI; | |
return function ( radians ) { | |
return radians * radianToDegreesFactor; | |
}; | |
}(), | |
isPowerOfTwo: function ( value ) { | |
return ( value & ( value - 1 ) ) === 0 && value !== 0; | |
}, | |
nearestPowerOfTwo: function ( value ) { | |
return Math.pow( 2, Math.round( Math.log( value ) / Math.LN2 ) ); | |
}, | |
nextPowerOfTwo: function ( value ) { | |
value --; | |
value |= value >> 1; | |
value |= value >> 2; | |
value |= value >> 4; | |
value |= value >> 8; | |
value |= value >> 16; | |
value ++; | |
return value; | |
} | |
}; | |
// File:src/math/Spline.js | |
/** | |
* Spline from Tween.js, slightly optimized (and trashed) | |
* http://sole.github.com/tween.js/examples/05_spline.html | |
* | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Spline = function ( points ) { | |
this.points = points; | |
var c = [], v3 = { x: 0, y: 0, z: 0 }, | |
point, intPoint, weight, w2, w3, | |
pa, pb, pc, pd; | |
this.initFromArray = function ( a ) { | |
this.points = []; | |
for ( var i = 0; i < a.length; i ++ ) { | |
this.points[ i ] = { x: a[ i ][ 0 ], y: a[ i ][ 1 ], z: a[ i ][ 2 ] }; | |
} | |
}; | |
this.getPoint = function ( k ) { | |
point = ( this.points.length - 1 ) * k; | |
intPoint = Math.floor( point ); | |
weight = point - intPoint; | |
c[ 0 ] = intPoint === 0 ? intPoint : intPoint - 1; | |
c[ 1 ] = intPoint; | |
c[ 2 ] = intPoint > this.points.length - 2 ? this.points.length - 1 : intPoint + 1; | |
c[ 3 ] = intPoint > this.points.length - 3 ? this.points.length - 1 : intPoint + 2; | |
pa = this.points[ c[ 0 ] ]; | |
pb = this.points[ c[ 1 ] ]; | |
pc = this.points[ c[ 2 ] ]; | |
pd = this.points[ c[ 3 ] ]; | |
w2 = weight * weight; | |
w3 = weight * w2; | |
v3.x = interpolate( pa.x, pb.x, pc.x, pd.x, weight, w2, w3 ); | |
v3.y = interpolate( pa.y, pb.y, pc.y, pd.y, weight, w2, w3 ); | |
v3.z = interpolate( pa.z, pb.z, pc.z, pd.z, weight, w2, w3 ); | |
return v3; | |
}; | |
this.getControlPointsArray = function () { | |
var i, p, l = this.points.length, | |
coords = []; | |
for ( i = 0; i < l; i ++ ) { | |
p = this.points[ i ]; | |
coords[ i ] = [ p.x, p.y, p.z ]; | |
} | |
return coords; | |
}; | |
// approximate length by summing linear segments | |
this.getLength = function ( nSubDivisions ) { | |
var i, index, nSamples, position, | |
point = 0, intPoint = 0, oldIntPoint = 0, | |
oldPosition = new THREE.Vector3(), | |
tmpVec = new THREE.Vector3(), | |
chunkLengths = [], | |
totalLength = 0; | |
// first point has 0 length | |
chunkLengths[ 0 ] = 0; | |
if ( ! nSubDivisions ) nSubDivisions = 100; | |
nSamples = this.points.length * nSubDivisions; | |
oldPosition.copy( this.points[ 0 ] ); | |
for ( i = 1; i < nSamples; i ++ ) { | |
index = i / nSamples; | |
position = this.getPoint( index ); | |
tmpVec.copy( position ); | |
totalLength += tmpVec.distanceTo( oldPosition ); | |
oldPosition.copy( position ); | |
point = ( this.points.length - 1 ) * index; | |
intPoint = Math.floor( point ); | |
if ( intPoint !== oldIntPoint ) { | |
chunkLengths[ intPoint ] = totalLength; | |
oldIntPoint = intPoint; | |
} | |
} | |
// last point ends with total length | |
chunkLengths[ chunkLengths.length ] = totalLength; | |
return { chunks: chunkLengths, total: totalLength }; | |
}; | |
this.reparametrizeByArcLength = function ( samplingCoef ) { | |
var i, j, | |
index, indexCurrent, indexNext, | |
realDistance, | |
sampling, position, | |
newpoints = [], | |
tmpVec = new THREE.Vector3(), | |
sl = this.getLength(); | |
newpoints.push( tmpVec.copy( this.points[ 0 ] ).clone() ); | |
for ( i = 1; i < this.points.length; i ++ ) { | |
//tmpVec.copy( this.points[ i - 1 ] ); | |
//linearDistance = tmpVec.distanceTo( this.points[ i ] ); | |
realDistance = sl.chunks[ i ] - sl.chunks[ i - 1 ]; | |
sampling = Math.ceil( samplingCoef * realDistance / sl.total ); | |
indexCurrent = ( i - 1 ) / ( this.points.length - 1 ); | |
indexNext = i / ( this.points.length - 1 ); | |
for ( j = 1; j < sampling - 1; j ++ ) { | |
index = indexCurrent + j * ( 1 / sampling ) * ( indexNext - indexCurrent ); | |
position = this.getPoint( index ); | |
newpoints.push( tmpVec.copy( position ).clone() ); | |
} | |
newpoints.push( tmpVec.copy( this.points[ i ] ).clone() ); | |
} | |
this.points = newpoints; | |
}; | |
// Catmull-Rom | |
function interpolate( p0, p1, p2, p3, t, t2, t3 ) { | |
var v0 = ( p2 - p0 ) * 0.5, | |
v1 = ( p3 - p1 ) * 0.5; | |
return ( 2 * ( p1 - p2 ) + v0 + v1 ) * t3 + ( - 3 * ( p1 - p2 ) - 2 * v0 - v1 ) * t2 + v0 * t + p1; | |
} | |
}; | |
// File:src/math/Triangle.js | |
/** | |
* @author bhouston / http://clara.io | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Triangle = function ( a, b, c ) { | |
this.a = ( a !== undefined ) ? a : new THREE.Vector3(); | |
this.b = ( b !== undefined ) ? b : new THREE.Vector3(); | |
this.c = ( c !== undefined ) ? c : new THREE.Vector3(); | |
}; | |
THREE.Triangle.normal = function () { | |
var v0 = new THREE.Vector3(); | |
return function ( a, b, c, optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
result.subVectors( c, b ); | |
v0.subVectors( a, b ); | |
result.cross( v0 ); | |
var resultLengthSq = result.lengthSq(); | |
if ( resultLengthSq > 0 ) { | |
return result.multiplyScalar( 1 / Math.sqrt( resultLengthSq ) ); | |
} | |
return result.set( 0, 0, 0 ); | |
}; | |
}(); | |
// static/instance method to calculate barycentric coordinates | |
// based on: http://www.blackpawn.com/texts/pointinpoly/default.html | |
THREE.Triangle.barycoordFromPoint = function () { | |
var v0 = new THREE.Vector3(); | |
var v1 = new THREE.Vector3(); | |
var v2 = new THREE.Vector3(); | |
return function ( point, a, b, c, optionalTarget ) { | |
v0.subVectors( c, a ); | |
v1.subVectors( b, a ); | |
v2.subVectors( point, a ); | |
var dot00 = v0.dot( v0 ); | |
var dot01 = v0.dot( v1 ); | |
var dot02 = v0.dot( v2 ); | |
var dot11 = v1.dot( v1 ); | |
var dot12 = v1.dot( v2 ); | |
var denom = ( dot00 * dot11 - dot01 * dot01 ); | |
var result = optionalTarget || new THREE.Vector3(); | |
// collinear or singular triangle | |
if ( denom === 0 ) { | |
// arbitrary location outside of triangle? | |
// not sure if this is the best idea, maybe should be returning undefined | |
return result.set( - 2, - 1, - 1 ); | |
} | |
var invDenom = 1 / denom; | |
var u = ( dot11 * dot02 - dot01 * dot12 ) * invDenom; | |
var v = ( dot00 * dot12 - dot01 * dot02 ) * invDenom; | |
// barycentric coordinates must always sum to 1 | |
return result.set( 1 - u - v, v, u ); | |
}; | |
}(); | |
THREE.Triangle.containsPoint = function () { | |
var v1 = new THREE.Vector3(); | |
return function ( point, a, b, c ) { | |
var result = THREE.Triangle.barycoordFromPoint( point, a, b, c, v1 ); | |
return ( result.x >= 0 ) && ( result.y >= 0 ) && ( ( result.x + result.y ) <= 1 ); | |
}; | |
}(); | |
THREE.Triangle.prototype = { | |
constructor: THREE.Triangle, | |
set: function ( a, b, c ) { | |
this.a.copy( a ); | |
this.b.copy( b ); | |
this.c.copy( c ); | |
return this; | |
}, | |
setFromPointsAndIndices: function ( points, i0, i1, i2 ) { | |
this.a.copy( points[ i0 ] ); | |
this.b.copy( points[ i1 ] ); | |
this.c.copy( points[ i2 ] ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( triangle ) { | |
this.a.copy( triangle.a ); | |
this.b.copy( triangle.b ); | |
this.c.copy( triangle.c ); | |
return this; | |
}, | |
area: function () { | |
var v0 = new THREE.Vector3(); | |
var v1 = new THREE.Vector3(); | |
return function () { | |
v0.subVectors( this.c, this.b ); | |
v1.subVectors( this.a, this.b ); | |
return v0.cross( v1 ).length() * 0.5; | |
}; | |
}(), | |
midpoint: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
return result.addVectors( this.a, this.b ).add( this.c ).multiplyScalar( 1 / 3 ); | |
}, | |
normal: function ( optionalTarget ) { | |
return THREE.Triangle.normal( this.a, this.b, this.c, optionalTarget ); | |
}, | |
plane: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Plane(); | |
return result.setFromCoplanarPoints( this.a, this.b, this.c ); | |
}, | |
barycoordFromPoint: function ( point, optionalTarget ) { | |
return THREE.Triangle.barycoordFromPoint( point, this.a, this.b, this.c, optionalTarget ); | |
}, | |
containsPoint: function ( point ) { | |
return THREE.Triangle.containsPoint( point, this.a, this.b, this.c ); | |
}, | |
equals: function ( triangle ) { | |
return triangle.a.equals( this.a ) && triangle.b.equals( this.b ) && triangle.c.equals( this.c ); | |
} | |
}; | |
// File:src/math/Interpolant.js | |
/** | |
* Abstract base class of interpolants over parametric samples. | |
* | |
* The parameter domain is one dimensional, typically the time or a path | |
* along a curve defined by the data. | |
* | |
* The sample values can have any dimensionality and derived classes may | |
* apply special interpretations to the data. | |
* | |
* This class provides the interval seek in a Template Method, deferring | |
* the actual interpolation to derived classes. | |
* | |
* Time complexity is O(1) for linear access crossing at most two points | |
* and O(log N) for random access, where N is the number of positions. | |
* | |
* References: | |
* | |
* http://www.oodesign.com/template-method-pattern.html | |
* | |
* @author tschw | |
*/ | |
THREE.Interpolant = function( | |
parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
this.parameterPositions = parameterPositions; | |
this._cachedIndex = 0; | |
this.resultBuffer = resultBuffer !== undefined ? | |
resultBuffer : new sampleValues.constructor( sampleSize ); | |
this.sampleValues = sampleValues; | |
this.valueSize = sampleSize; | |
}; | |
THREE.Interpolant.prototype = { | |
constructor: THREE.Interpolant, | |
evaluate: function( t ) { | |
var pp = this.parameterPositions, | |
i1 = this._cachedIndex, | |
t1 = pp[ i1 ], | |
t0 = pp[ i1 - 1 ]; | |
validate_interval: { | |
seek: { | |
var right; | |
linear_scan: { | |
//- See http://jsperf.com/comparison-to-undefined/3 | |
//- slower code: | |
//- | |
//- if ( t >= t1 || t1 === undefined ) { | |
forward_scan: if ( ! ( t < t1 ) ) { | |
for ( var giveUpAt = i1 + 2; ;) { | |
if ( t1 === undefined ) { | |
if ( t < t0 ) break forward_scan; | |
// after end | |
i1 = pp.length; | |
this._cachedIndex = i1; | |
return this.afterEnd_( i1 - 1, t, t0 ); | |
} | |
if ( i1 === giveUpAt ) break; // this loop | |
t0 = t1; | |
t1 = pp[ ++ i1 ]; | |
if ( t < t1 ) { | |
// we have arrived at the sought interval | |
break seek; | |
} | |
} | |
// prepare binary search on the right side of the index | |
right = pp.length; | |
break linear_scan; | |
} | |
//- slower code: | |
//- if ( t < t0 || t0 === undefined ) { | |
if ( ! ( t >= t0 ) ) { | |
// looping? | |
var t1global = pp[ 1 ]; | |
if ( t < t1global ) { | |
i1 = 2; // + 1, using the scan for the details | |
t0 = t1global; | |
} | |
// linear reverse scan | |
for ( var giveUpAt = i1 - 2; ;) { | |
if ( t0 === undefined ) { | |
// before start | |
this._cachedIndex = 0; | |
return this.beforeStart_( 0, t, t1 ); | |
} | |
if ( i1 === giveUpAt ) break; // this loop | |
t1 = t0; | |
t0 = pp[ -- i1 - 1 ]; | |
if ( t >= t0 ) { | |
// we have arrived at the sought interval | |
break seek; | |
} | |
} | |
// prepare binary search on the left side of the index | |
right = i1; | |
i1 = 0; | |
break linear_scan; | |
} | |
// the interval is valid | |
break validate_interval; | |
} // linear scan | |
// binary search | |
while ( i1 < right ) { | |
var mid = ( i1 + right ) >>> 1; | |
if ( t < pp[ mid ] ) { | |
right = mid; | |
} else { | |
i1 = mid + 1; | |
} | |
} | |
t1 = pp[ i1 ]; | |
t0 = pp[ i1 - 1 ]; | |
// check boundary cases, again | |
if ( t0 === undefined ) { | |
this._cachedIndex = 0; | |
return this.beforeStart_( 0, t, t1 ); | |
} | |
if ( t1 === undefined ) { | |
i1 = pp.length; | |
this._cachedIndex = i1; | |
return this.afterEnd_( i1 - 1, t0, t ); | |
} | |
} // seek | |
this._cachedIndex = i1; | |
this.intervalChanged_( i1, t0, t1 ); | |
} // validate_interval | |
return this.interpolate_( i1, t0, t, t1 ); | |
}, | |
settings: null, // optional, subclass-specific settings structure | |
// Note: The indirection allows central control of many interpolants. | |
// --- Protected interface | |
DefaultSettings_: {}, | |
getSettings_: function() { | |
return this.settings || this.DefaultSettings_; | |
}, | |
copySampleValue_: function( index ) { | |
// copies a sample value to the result buffer | |
var result = this.resultBuffer, | |
values = this.sampleValues, | |
stride = this.valueSize, | |
offset = index * stride; | |
for ( var i = 0; i !== stride; ++ i ) { | |
result[ i ] = values[ offset + i ]; | |
} | |
return result; | |
}, | |
// Template methods for derived classes: | |
interpolate_: function( i1, t0, t, t1 ) { | |
throw new Error( "call to abstract method" ); | |
// implementations shall return this.resultBuffer | |
}, | |
intervalChanged_: function( i1, t0, t1 ) { | |
// empty | |
} | |
}; | |
Object.assign( THREE.Interpolant.prototype, { | |
beforeStart_: //( 0, t, t0 ), returns this.resultBuffer | |
THREE.Interpolant.prototype.copySampleValue_, | |
afterEnd_: //( N-1, tN-1, t ), returns this.resultBuffer | |
THREE.Interpolant.prototype.copySampleValue_ | |
} ); | |
// File:src/math/interpolants/CubicInterpolant.js | |
/** | |
* Fast and simple cubic spline interpolant. | |
* | |
* It was derived from a Hermitian construction setting the first derivative | |
* at each sample position to the linear slope between neighboring positions | |
* over their parameter interval. | |
* | |
* @author tschw | |
*/ | |
THREE.CubicInterpolant = function( | |
parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
THREE.Interpolant.call( | |
this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
this._weightPrev = -0; | |
this._offsetPrev = -0; | |
this._weightNext = -0; | |
this._offsetNext = -0; | |
}; | |
THREE.CubicInterpolant.prototype = | |
Object.assign( Object.create( THREE.Interpolant.prototype ), { | |
constructor: THREE.CubicInterpolant, | |
DefaultSettings_: { | |
endingStart: THREE.ZeroCurvatureEnding, | |
endingEnd: THREE.ZeroCurvatureEnding | |
}, | |
intervalChanged_: function( i1, t0, t1 ) { | |
var pp = this.parameterPositions, | |
iPrev = i1 - 2, | |
iNext = i1 + 1, | |
tPrev = pp[ iPrev ], | |
tNext = pp[ iNext ]; | |
if ( tPrev === undefined ) { | |
switch ( this.getSettings_().endingStart ) { | |
case THREE.ZeroSlopeEnding: | |
// f'(t0) = 0 | |
iPrev = i1; | |
tPrev = 2 * t0 - t1; | |
break; | |
case THREE.WrapAroundEnding: | |
// use the other end of the curve | |
iPrev = pp.length - 2; | |
tPrev = t0 + pp[ iPrev ] - pp[ iPrev + 1 ]; | |
break; | |
default: // ZeroCurvatureEnding | |
// f''(t0) = 0 a.k.a. Natural Spline | |
iPrev = i1; | |
tPrev = t1; | |
} | |
} | |
if ( tNext === undefined ) { | |
switch ( this.getSettings_().endingEnd ) { | |
case THREE.ZeroSlopeEnding: | |
// f'(tN) = 0 | |
iNext = i1; | |
tNext = 2 * t1 - t0; | |
break; | |
case THREE.WrapAroundEnding: | |
// use the other end of the curve | |
iNext = 1; | |
tNext = t1 + pp[ 1 ] - pp[ 0 ]; | |
break; | |
default: // ZeroCurvatureEnding | |
// f''(tN) = 0, a.k.a. Natural Spline | |
iNext = i1 - 1; | |
tNext = t0; | |
} | |
} | |
var halfDt = ( t1 - t0 ) * 0.5, | |
stride = this.valueSize; | |
this._weightPrev = halfDt / ( t0 - tPrev ); | |
this._weightNext = halfDt / ( tNext - t1 ); | |
this._offsetPrev = iPrev * stride; | |
this._offsetNext = iNext * stride; | |
}, | |
interpolate_: function( i1, t0, t, t1 ) { | |
var result = this.resultBuffer, | |
values = this.sampleValues, | |
stride = this.valueSize, | |
o1 = i1 * stride, o0 = o1 - stride, | |
oP = this._offsetPrev, oN = this._offsetNext, | |
wP = this._weightPrev, wN = this._weightNext, | |
p = ( t - t0 ) / ( t1 - t0 ), | |
pp = p * p, | |
ppp = pp * p; | |
// evaluate polynomials | |
var sP = - wP * ppp + 2 * wP * pp - wP * p; | |
var s0 = ( 1 + wP ) * ppp + (-1.5 - 2 * wP ) * pp + ( -0.5 + wP ) * p + 1; | |
var s1 = (-1 - wN ) * ppp + ( 1.5 + wN ) * pp + 0.5 * p; | |
var sN = wN * ppp - wN * pp; | |
// combine data linearly | |
for ( var i = 0; i !== stride; ++ i ) { | |
result[ i ] = | |
sP * values[ oP + i ] + | |
s0 * values[ o0 + i ] + | |
s1 * values[ o1 + i ] + | |
sN * values[ oN + i ]; | |
} | |
return result; | |
} | |
} ); | |
// File:src/math/interpolants/DiscreteInterpolant.js | |
/** | |
* | |
* Interpolant that evaluates to the sample value at the position preceeding | |
* the parameter. | |
* | |
* @author tschw | |
*/ | |
THREE.DiscreteInterpolant = function( | |
parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
THREE.Interpolant.call( | |
this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
}; | |
THREE.DiscreteInterpolant.prototype = | |
Object.assign( Object.create( THREE.Interpolant.prototype ), { | |
constructor: THREE.DiscreteInterpolant, | |
interpolate_: function( i1, t0, t, t1 ) { | |
return this.copySampleValue_( i1 - 1 ); | |
} | |
} ); | |
// File:src/math/interpolants/LinearInterpolant.js | |
/** | |
* @author tschw | |
*/ | |
THREE.LinearInterpolant = function( | |
parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
THREE.Interpolant.call( | |
this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
}; | |
THREE.LinearInterpolant.prototype = | |
Object.assign( Object.create( THREE.Interpolant.prototype ), { | |
constructor: THREE.LinearInterpolant, | |
interpolate_: function( i1, t0, t, t1 ) { | |
var result = this.resultBuffer, | |
values = this.sampleValues, | |
stride = this.valueSize, | |
offset1 = i1 * stride, | |
offset0 = offset1 - stride, | |
weight1 = ( t - t0 ) / ( t1 - t0 ), | |
weight0 = 1 - weight1; | |
for ( var i = 0; i !== stride; ++ i ) { | |
result[ i ] = | |
values[ offset0 + i ] * weight0 + | |
values[ offset1 + i ] * weight1; | |
} | |
return result; | |
} | |
} ); | |
// File:src/math/interpolants/QuaternionLinearInterpolant.js | |
/** | |
* Spherical linear unit quaternion interpolant. | |
* | |
* @author tschw | |
*/ | |
THREE.QuaternionLinearInterpolant = function( | |
parameterPositions, sampleValues, sampleSize, resultBuffer ) { | |
THREE.Interpolant.call( | |
this, parameterPositions, sampleValues, sampleSize, resultBuffer ); | |
}; | |
THREE.QuaternionLinearInterpolant.prototype = | |
Object.assign( Object.create( THREE.Interpolant.prototype ), { | |
constructor: THREE.QuaternionLinearInterpolant, | |
interpolate_: function( i1, t0, t, t1 ) { | |
var result = this.resultBuffer, | |
values = this.sampleValues, | |
stride = this.valueSize, | |
offset = i1 * stride, | |
alpha = ( t - t0 ) / ( t1 - t0 ); | |
for ( var end = offset + stride; offset !== end; offset += 4 ) { | |
THREE.Quaternion.slerpFlat( result, 0, | |
values, offset - stride, values, offset, alpha ); | |
} | |
return result; | |
} | |
} ); | |
// File:src/core/Clock.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Clock = function ( autoStart ) { | |
this.autoStart = ( autoStart !== undefined ) ? autoStart : true; | |
this.startTime = 0; | |
this.oldTime = 0; | |
this.elapsedTime = 0; | |
this.running = false; | |
}; | |
THREE.Clock.prototype = { | |
constructor: THREE.Clock, | |
start: function () { | |
this.startTime = performance.now(); | |
this.oldTime = this.startTime; | |
this.running = true; | |
}, | |
stop: function () { | |
this.getElapsedTime(); | |
this.running = false; | |
}, | |
getElapsedTime: function () { | |
this.getDelta(); | |
return this.elapsedTime; | |
}, | |
getDelta: function () { | |
var diff = 0; | |
if ( this.autoStart && ! this.running ) { | |
this.start(); | |
} | |
if ( this.running ) { | |
var newTime = performance.now(); | |
diff = 0.001 * ( newTime - this.oldTime ); | |
this.oldTime = newTime; | |
this.elapsedTime += diff; | |
} | |
return diff; | |
} | |
}; | |
// File:src/core/EventDispatcher.js | |
/** | |
* https://github.com/mrdoob/eventdispatcher.js/ | |
*/ | |
THREE.EventDispatcher = function () {}; | |
THREE.EventDispatcher.prototype = { | |
constructor: THREE.EventDispatcher, | |
apply: function ( object ) { | |
object.addEventListener = THREE.EventDispatcher.prototype.addEventListener; | |
object.hasEventListener = THREE.EventDispatcher.prototype.hasEventListener; | |
object.removeEventListener = THREE.EventDispatcher.prototype.removeEventListener; | |
object.dispatchEvent = THREE.EventDispatcher.prototype.dispatchEvent; | |
}, | |
addEventListener: function ( type, listener ) { | |
if ( this._listeners === undefined ) this._listeners = {}; | |
var listeners = this._listeners; | |
if ( listeners[ type ] === undefined ) { | |
listeners[ type ] = []; | |
} | |
if ( listeners[ type ].indexOf( listener ) === - 1 ) { | |
listeners[ type ].push( listener ); | |
} | |
}, | |
hasEventListener: function ( type, listener ) { | |
if ( this._listeners === undefined ) return false; | |
var listeners = this._listeners; | |
if ( listeners[ type ] !== undefined && listeners[ type ].indexOf( listener ) !== - 1 ) { | |
return true; | |
} | |
return false; | |
}, | |
removeEventListener: function ( type, listener ) { | |
if ( this._listeners === undefined ) return; | |
var listeners = this._listeners; | |
var listenerArray = listeners[ type ]; | |
if ( listenerArray !== undefined ) { | |
var index = listenerArray.indexOf( listener ); | |
if ( index !== - 1 ) { | |
listenerArray.splice( index, 1 ); | |
} | |
} | |
}, | |
dispatchEvent: function ( event ) { | |
if ( this._listeners === undefined ) return; | |
var listeners = this._listeners; | |
var listenerArray = listeners[ event.type ]; | |
if ( listenerArray !== undefined ) { | |
event.target = this; | |
var array = []; | |
var length = listenerArray.length; | |
for ( var i = 0; i < length; i ++ ) { | |
array[ i ] = listenerArray[ i ]; | |
} | |
for ( var i = 0; i < length; i ++ ) { | |
array[ i ].call( this, event ); | |
} | |
} | |
} | |
}; | |
// File:src/core/Layers.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Layers = function () { | |
this.mask = 1; | |
}; | |
THREE.Layers.prototype = { | |
constructor: THREE.Layers, | |
set: function ( channel ) { | |
this.mask = 1 << channel; | |
}, | |
enable: function ( channel ) { | |
this.mask |= 1 << channel; | |
}, | |
toggle: function ( channel ) { | |
this.mask ^= 1 << channel; | |
}, | |
disable: function ( channel ) { | |
this.mask &= ~ ( 1 << channel ); | |
}, | |
test: function ( layers ) { | |
return ( this.mask & layers.mask ) !== 0; | |
} | |
}; | |
// File:src/core/Raycaster.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author bhouston / http://clara.io/ | |
* @author stephomi / http://stephaneginier.com/ | |
*/ | |
( function ( THREE ) { | |
THREE.Raycaster = function ( origin, direction, near, far ) { | |
this.ray = new THREE.Ray( origin, direction ); | |
// direction is assumed to be normalized (for accurate distance calculations) | |
this.near = near || 0; | |
this.far = far || Infinity; | |
this.params = { | |
Mesh: {}, | |
Line: {}, | |
LOD: {}, | |
Points: { threshold: 1 }, | |
Sprite: {} | |
}; | |
Object.defineProperties( this.params, { | |
PointCloud: { | |
get: function () { | |
console.warn( 'THREE.Raycaster: params.PointCloud has been renamed to params.Points.' ); | |
return this.Points; | |
} | |
} | |
} ); | |
}; | |
function ascSort( a, b ) { | |
return a.distance - b.distance; | |
} | |
function intersectObject( object, raycaster, intersects, recursive ) { | |
if ( object.visible === false ) return; | |
object.raycast( raycaster, intersects ); | |
if ( recursive === true ) { | |
var children = object.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
intersectObject( children[ i ], raycaster, intersects, true ); | |
} | |
} | |
} | |
// | |
THREE.Raycaster.prototype = { | |
constructor: THREE.Raycaster, | |
linePrecision: 1, | |
set: function ( origin, direction ) { | |
// direction is assumed to be normalized (for accurate distance calculations) | |
this.ray.set( origin, direction ); | |
}, | |
setFromCamera: function ( coords, camera ) { | |
if ( camera instanceof THREE.PerspectiveCamera ) { | |
this.ray.origin.setFromMatrixPosition( camera.matrixWorld ); | |
this.ray.direction.set( coords.x, coords.y, 0.5 ).unproject( camera ).sub( this.ray.origin ).normalize(); | |
} else if ( camera instanceof THREE.OrthographicCamera ) { | |
this.ray.origin.set( coords.x, coords.y, - 1 ).unproject( camera ); | |
this.ray.direction.set( 0, 0, - 1 ).transformDirection( camera.matrixWorld ); | |
} else { | |
console.error( 'THREE.Raycaster: Unsupported camera type.' ); | |
} | |
}, | |
intersectObject: function ( object, recursive ) { | |
var intersects = []; | |
intersectObject( object, this, intersects, recursive ); | |
intersects.sort( ascSort ); | |
return intersects; | |
}, | |
intersectObjects: function ( objects, recursive ) { | |
var intersects = []; | |
if ( Array.isArray( objects ) === false ) { | |
console.warn( 'THREE.Raycaster.intersectObjects: objects is not an Array.' ); | |
return intersects; | |
} | |
for ( var i = 0, l = objects.length; i < l; i ++ ) { | |
intersectObject( objects[ i ], this, intersects, recursive ); | |
} | |
intersects.sort( ascSort ); | |
return intersects; | |
} | |
}; | |
}( THREE ) ); | |
// File:src/core/Object3D.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author WestLangley / http://github.com/WestLangley | |
* @author elephantatwork / www.elephantatwork.ch | |
*/ | |
THREE.Object3D = function () { | |
Object.defineProperty( this, 'id', { value: THREE.Object3DIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.type = 'Object3D'; | |
this.parent = null; | |
this.children = []; | |
this.up = THREE.Object3D.DefaultUp.clone(); | |
var position = new THREE.Vector3(); | |
var rotation = new THREE.Euler(); | |
var quaternion = new THREE.Quaternion(); | |
var scale = new THREE.Vector3( 1, 1, 1 ); | |
function onRotationChange() { | |
quaternion.setFromEuler( rotation, false ); | |
} | |
function onQuaternionChange() { | |
rotation.setFromQuaternion( quaternion, undefined, false ); | |
} | |
rotation.onChange( onRotationChange ); | |
quaternion.onChange( onQuaternionChange ); | |
Object.defineProperties( this, { | |
position: { | |
enumerable: true, | |
value: position | |
}, | |
rotation: { | |
enumerable: true, | |
value: rotation | |
}, | |
quaternion: { | |
enumerable: true, | |
value: quaternion | |
}, | |
scale: { | |
enumerable: true, | |
value: scale | |
}, | |
modelViewMatrix: { | |
value: new THREE.Matrix4() | |
}, | |
normalMatrix: { | |
value: new THREE.Matrix3() | |
} | |
} ); | |
this.rotationAutoUpdate = true; | |
this.matrix = new THREE.Matrix4(); | |
this.matrixWorld = new THREE.Matrix4(); | |
this.matrixAutoUpdate = THREE.Object3D.DefaultMatrixAutoUpdate; | |
this.matrixWorldNeedsUpdate = false; | |
this.layers = new THREE.Layers(); | |
this.visible = true; | |
this.castShadow = false; | |
this.receiveShadow = false; | |
this.frustumCulled = true; | |
this.renderOrder = 0; | |
this.userData = {}; | |
}; | |
THREE.Object3D.DefaultUp = new THREE.Vector3( 0, 1, 0 ); | |
THREE.Object3D.DefaultMatrixAutoUpdate = true; | |
THREE.Object3D.prototype = { | |
constructor: THREE.Object3D, | |
applyMatrix: function ( matrix ) { | |
this.matrix.multiplyMatrices( matrix, this.matrix ); | |
this.matrix.decompose( this.position, this.quaternion, this.scale ); | |
}, | |
setRotationFromAxisAngle: function ( axis, angle ) { | |
// assumes axis is normalized | |
this.quaternion.setFromAxisAngle( axis, angle ); | |
}, | |
setRotationFromEuler: function ( euler ) { | |
this.quaternion.setFromEuler( euler, true ); | |
}, | |
setRotationFromMatrix: function ( m ) { | |
// assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled) | |
this.quaternion.setFromRotationMatrix( m ); | |
}, | |
setRotationFromQuaternion: function ( q ) { | |
// assumes q is normalized | |
this.quaternion.copy( q ); | |
}, | |
rotateOnAxis: function () { | |
// rotate object on axis in object space | |
// axis is assumed to be normalized | |
var q1 = new THREE.Quaternion(); | |
return function ( axis, angle ) { | |
q1.setFromAxisAngle( axis, angle ); | |
this.quaternion.multiply( q1 ); | |
return this; | |
}; | |
}(), | |
rotateX: function () { | |
var v1 = new THREE.Vector3( 1, 0, 0 ); | |
return function ( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
rotateY: function () { | |
var v1 = new THREE.Vector3( 0, 1, 0 ); | |
return function ( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
rotateZ: function () { | |
var v1 = new THREE.Vector3( 0, 0, 1 ); | |
return function ( angle ) { | |
return this.rotateOnAxis( v1, angle ); | |
}; | |
}(), | |
translateOnAxis: function () { | |
// translate object by distance along axis in object space | |
// axis is assumed to be normalized | |
var v1 = new THREE.Vector3(); | |
return function ( axis, distance ) { | |
v1.copy( axis ).applyQuaternion( this.quaternion ); | |
this.position.add( v1.multiplyScalar( distance ) ); | |
return this; | |
}; | |
}(), | |
translateX: function () { | |
var v1 = new THREE.Vector3( 1, 0, 0 ); | |
return function ( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
translateY: function () { | |
var v1 = new THREE.Vector3( 0, 1, 0 ); | |
return function ( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
translateZ: function () { | |
var v1 = new THREE.Vector3( 0, 0, 1 ); | |
return function ( distance ) { | |
return this.translateOnAxis( v1, distance ); | |
}; | |
}(), | |
localToWorld: function ( vector ) { | |
return vector.applyMatrix4( this.matrixWorld ); | |
}, | |
worldToLocal: function () { | |
var m1 = new THREE.Matrix4(); | |
return function ( vector ) { | |
return vector.applyMatrix4( m1.getInverse( this.matrixWorld ) ); | |
}; | |
}(), | |
lookAt: function () { | |
// This routine does not support objects with rotated and/or translated parent(s) | |
var m1 = new THREE.Matrix4(); | |
return function ( vector ) { | |
m1.lookAt( vector, this.position, this.up ); | |
this.quaternion.setFromRotationMatrix( m1 ); | |
}; | |
}(), | |
add: function ( object ) { | |
if ( arguments.length > 1 ) { | |
for ( var i = 0; i < arguments.length; i ++ ) { | |
this.add( arguments[ i ] ); | |
} | |
return this; | |
} | |
if ( object === this ) { | |
console.error( "THREE.Object3D.add: object can't be added as a child of itself.", object ); | |
return this; | |
} | |
if ( object instanceof THREE.Object3D ) { | |
if ( object.parent !== null ) { | |
object.parent.remove( object ); | |
} | |
object.parent = this; | |
object.dispatchEvent( { type: 'added' } ); | |
this.children.push( object ); | |
} else { | |
console.error( "THREE.Object3D.add: object not an instance of THREE.Object3D.", object ); | |
} | |
return this; | |
}, | |
remove: function ( object ) { | |
if ( arguments.length > 1 ) { | |
for ( var i = 0; i < arguments.length; i ++ ) { | |
this.remove( arguments[ i ] ); | |
} | |
} | |
var index = this.children.indexOf( object ); | |
if ( index !== - 1 ) { | |
object.parent = null; | |
object.dispatchEvent( { type: 'removed' } ); | |
this.children.splice( index, 1 ); | |
} | |
}, | |
getObjectById: function ( id ) { | |
return this.getObjectByProperty( 'id', id ); | |
}, | |
getObjectByName: function ( name ) { | |
return this.getObjectByProperty( 'name', name ); | |
}, | |
getObjectByProperty: function ( name, value ) { | |
if ( this[ name ] === value ) return this; | |
for ( var i = 0, l = this.children.length; i < l; i ++ ) { | |
var child = this.children[ i ]; | |
var object = child.getObjectByProperty( name, value ); | |
if ( object !== undefined ) { | |
return object; | |
} | |
} | |
return undefined; | |
}, | |
getWorldPosition: function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
this.updateMatrixWorld( true ); | |
return result.setFromMatrixPosition( this.matrixWorld ); | |
}, | |
getWorldQuaternion: function () { | |
var position = new THREE.Vector3(); | |
var scale = new THREE.Vector3(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Quaternion(); | |
this.updateMatrixWorld( true ); | |
this.matrixWorld.decompose( position, result, scale ); | |
return result; | |
}; | |
}(), | |
getWorldRotation: function () { | |
var quaternion = new THREE.Quaternion(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Euler(); | |
this.getWorldQuaternion( quaternion ); | |
return result.setFromQuaternion( quaternion, this.rotation.order, false ); | |
}; | |
}(), | |
getWorldScale: function () { | |
var position = new THREE.Vector3(); | |
var quaternion = new THREE.Quaternion(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
this.updateMatrixWorld( true ); | |
this.matrixWorld.decompose( position, quaternion, result ); | |
return result; | |
}; | |
}(), | |
getWorldDirection: function () { | |
var quaternion = new THREE.Quaternion(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
this.getWorldQuaternion( quaternion ); | |
return result.set( 0, 0, 1 ).applyQuaternion( quaternion ); | |
}; | |
}(), | |
raycast: function () {}, | |
traverse: function ( callback ) { | |
callback( this ); | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].traverse( callback ); | |
} | |
}, | |
traverseVisible: function ( callback ) { | |
if ( this.visible === false ) return; | |
callback( this ); | |
var children = this.children; | |
for ( var i = 0, l = children.length; i < l; i ++ ) { | |
children[ i ].traverseVisible( callback ); | |
} | |
}, | |
traverseAncestors: function ( callback ) { | |
var parent = this.parent; | |
if ( parent !== null ) { | |
callback( parent ); | |
parent.traverseAncestors( callback ); | |
} | |
}, | |
updateMatrix: function () { | |
this.matrix.compose( this.position, this.quaternion, this.scale ); | |
this.matrixWorldNeedsUpdate = true; | |
}, | |
updateMatrixWorld: function ( force ) { | |
if ( this.matrixAutoUpdate === true ) this.updateMatrix(); | |
if ( this.matrixWorldNeedsUpdate === true || force === true ) { | |
if ( this.parent === null ) { | |
this.matrixWorld.copy( this.matrix ); | |
} else { | |
this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix ); | |
} | |
this.matrixWorldNeedsUpdate = false; | |
force = true; | |
} | |
// update children | |
for ( var i = 0, l = this.children.length; i < l; i ++ ) { | |
this.children[ i ].updateMatrixWorld( force ); | |
} | |
}, | |
toJSON: function ( meta ) { | |
var isRootObject = ( meta === undefined ); | |
var output = {}; | |
// meta is a hash used to collect geometries, materials. | |
// not providing it implies that this is the root object | |
// being serialized. | |
if ( isRootObject ) { | |
// initialize meta obj | |
meta = { | |
geometries: {}, | |
materials: {}, | |
textures: {}, | |
images: {} | |
}; | |
output.metadata = { | |
version: 4.4, | |
type: 'Object', | |
generator: 'Object3D.toJSON' | |
}; | |
} | |
// standard Object3D serialization | |
var object = {}; | |
object.uuid = this.uuid; | |
object.type = this.type; | |
if ( this.name !== '' ) object.name = this.name; | |
if ( JSON.stringify( this.userData ) !== '{}' ) object.userData = this.userData; | |
if ( this.castShadow === true ) object.castShadow = true; | |
if ( this.receiveShadow === true ) object.receiveShadow = true; | |
if ( this.visible === false ) object.visible = false; | |
object.matrix = this.matrix.toArray(); | |
// | |
if ( this.geometry !== undefined ) { | |
if ( meta.geometries[ this.geometry.uuid ] === undefined ) { | |
meta.geometries[ this.geometry.uuid ] = this.geometry.toJSON( meta ); | |
} | |
object.geometry = this.geometry.uuid; | |
} | |
if ( this.material !== undefined ) { | |
if ( meta.materials[ this.material.uuid ] === undefined ) { | |
meta.materials[ this.material.uuid ] = this.material.toJSON( meta ); | |
} | |
object.material = this.material.uuid; | |
} | |
// | |
if ( this.children.length > 0 ) { | |
object.children = []; | |
for ( var i = 0; i < this.children.length; i ++ ) { | |
object.children.push( this.children[ i ].toJSON( meta ).object ); | |
} | |
} | |
if ( isRootObject ) { | |
var geometries = extractFromCache( meta.geometries ); | |
var materials = extractFromCache( meta.materials ); | |
var textures = extractFromCache( meta.textures ); | |
var images = extractFromCache( meta.images ); | |
if ( geometries.length > 0 ) output.geometries = geometries; | |
if ( materials.length > 0 ) output.materials = materials; | |
if ( textures.length > 0 ) output.textures = textures; | |
if ( images.length > 0 ) output.images = images; | |
} | |
output.object = object; | |
return output; | |
// extract data from the cache hash | |
// remove metadata on each item | |
// and return as array | |
function extractFromCache ( cache ) { | |
var values = []; | |
for ( var key in cache ) { | |
var data = cache[ key ]; | |
delete data.metadata; | |
values.push( data ); | |
} | |
return values; | |
} | |
}, | |
clone: function ( recursive ) { | |
return new this.constructor().copy( this, recursive ); | |
}, | |
copy: function ( source, recursive ) { | |
if ( recursive === undefined ) recursive = true; | |
this.name = source.name; | |
this.up.copy( source.up ); | |
this.position.copy( source.position ); | |
this.quaternion.copy( source.quaternion ); | |
this.scale.copy( source.scale ); | |
this.rotationAutoUpdate = source.rotationAutoUpdate; | |
this.matrix.copy( source.matrix ); | |
this.matrixWorld.copy( source.matrixWorld ); | |
this.matrixAutoUpdate = source.matrixAutoUpdate; | |
this.matrixWorldNeedsUpdate = source.matrixWorldNeedsUpdate; | |
this.visible = source.visible; | |
this.castShadow = source.castShadow; | |
this.receiveShadow = source.receiveShadow; | |
this.frustumCulled = source.frustumCulled; | |
this.renderOrder = source.renderOrder; | |
this.userData = JSON.parse( JSON.stringify( source.userData ) ); | |
if ( recursive === true ) { | |
for ( var i = 0; i < source.children.length; i ++ ) { | |
var child = source.children[ i ]; | |
this.add( child.clone() ); | |
} | |
} | |
return this; | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.Object3D.prototype ); | |
THREE.Object3DIdCount = 0; | |
// File:src/core/Face3.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Face3 = function ( a, b, c, normal, color, materialIndex ) { | |
this.a = a; | |
this.b = b; | |
this.c = c; | |
this.normal = normal instanceof THREE.Vector3 ? normal : new THREE.Vector3(); | |
this.vertexNormals = Array.isArray( normal ) ? normal : []; | |
this.color = color instanceof THREE.Color ? color : new THREE.Color(); | |
this.vertexColors = Array.isArray( color ) ? color : []; | |
this.materialIndex = materialIndex !== undefined ? materialIndex : 0; | |
}; | |
THREE.Face3.prototype = { | |
constructor: THREE.Face3, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( source ) { | |
this.a = source.a; | |
this.b = source.b; | |
this.c = source.c; | |
this.normal.copy( source.normal ); | |
this.color.copy( source.color ); | |
this.materialIndex = source.materialIndex; | |
for ( var i = 0, il = source.vertexNormals.length; i < il; i ++ ) { | |
this.vertexNormals[ i ] = source.vertexNormals[ i ].clone(); | |
} | |
for ( var i = 0, il = source.vertexColors.length; i < il; i ++ ) { | |
this.vertexColors[ i ] = source.vertexColors[ i ].clone(); | |
} | |
return this; | |
} | |
}; | |
// File:src/core/BufferAttribute.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.BufferAttribute = function ( array, itemSize ) { | |
this.uuid = THREE.Math.generateUUID(); | |
this.array = array; | |
this.itemSize = itemSize; | |
this.dynamic = false; | |
this.updateRange = { offset: 0, count: - 1 }; | |
this.version = 0; | |
}; | |
THREE.BufferAttribute.prototype = { | |
constructor: THREE.BufferAttribute, | |
get count() { | |
return this.array.length / this.itemSize; | |
}, | |
set needsUpdate( value ) { | |
if ( value === true ) this.version ++; | |
}, | |
setDynamic: function ( value ) { | |
this.dynamic = value; | |
return this; | |
}, | |
copy: function ( source ) { | |
this.array = new source.array.constructor( source.array ); | |
this.itemSize = source.itemSize; | |
this.dynamic = source.dynamic; | |
return this; | |
}, | |
copyAt: function ( index1, attribute, index2 ) { | |
index1 *= this.itemSize; | |
index2 *= attribute.itemSize; | |
for ( var i = 0, l = this.itemSize; i < l; i ++ ) { | |
this.array[ index1 + i ] = attribute.array[ index2 + i ]; | |
} | |
return this; | |
}, | |
copyArray: function ( array ) { | |
this.array.set( array ); | |
return this; | |
}, | |
copyColorsArray: function ( colors ) { | |
var array = this.array, offset = 0; | |
for ( var i = 0, l = colors.length; i < l; i ++ ) { | |
var color = colors[ i ]; | |
if ( color === undefined ) { | |
console.warn( 'THREE.BufferAttribute.copyColorsArray(): color is undefined', i ); | |
color = new THREE.Color(); | |
} | |
array[ offset ++ ] = color.r; | |
array[ offset ++ ] = color.g; | |
array[ offset ++ ] = color.b; | |
} | |
return this; | |
}, | |
copyIndicesArray: function ( indices ) { | |
var array = this.array, offset = 0; | |
for ( var i = 0, l = indices.length; i < l; i ++ ) { | |
var index = indices[ i ]; | |
array[ offset ++ ] = index.a; | |
array[ offset ++ ] = index.b; | |
array[ offset ++ ] = index.c; | |
} | |
return this; | |
}, | |
copyVector2sArray: function ( vectors ) { | |
var array = this.array, offset = 0; | |
for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
var vector = vectors[ i ]; | |
if ( vector === undefined ) { | |
console.warn( 'THREE.BufferAttribute.copyVector2sArray(): vector is undefined', i ); | |
vector = new THREE.Vector2(); | |
} | |
array[ offset ++ ] = vector.x; | |
array[ offset ++ ] = vector.y; | |
} | |
return this; | |
}, | |
copyVector3sArray: function ( vectors ) { | |
var array = this.array, offset = 0; | |
for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
var vector = vectors[ i ]; | |
if ( vector === undefined ) { | |
console.warn( 'THREE.BufferAttribute.copyVector3sArray(): vector is undefined', i ); | |
vector = new THREE.Vector3(); | |
} | |
array[ offset ++ ] = vector.x; | |
array[ offset ++ ] = vector.y; | |
array[ offset ++ ] = vector.z; | |
} | |
return this; | |
}, | |
copyVector4sArray: function ( vectors ) { | |
var array = this.array, offset = 0; | |
for ( var i = 0, l = vectors.length; i < l; i ++ ) { | |
var vector = vectors[ i ]; | |
if ( vector === undefined ) { | |
console.warn( 'THREE.BufferAttribute.copyVector4sArray(): vector is undefined', i ); | |
vector = new THREE.Vector4(); | |
} | |
array[ offset ++ ] = vector.x; | |
array[ offset ++ ] = vector.y; | |
array[ offset ++ ] = vector.z; | |
array[ offset ++ ] = vector.w; | |
} | |
return this; | |
}, | |
set: function ( value, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.array.set( value, offset ); | |
return this; | |
}, | |
getX: function ( index ) { | |
return this.array[ index * this.itemSize ]; | |
}, | |
setX: function ( index, x ) { | |
this.array[ index * this.itemSize ] = x; | |
return this; | |
}, | |
getY: function ( index ) { | |
return this.array[ index * this.itemSize + 1 ]; | |
}, | |
setY: function ( index, y ) { | |
this.array[ index * this.itemSize + 1 ] = y; | |
return this; | |
}, | |
getZ: function ( index ) { | |
return this.array[ index * this.itemSize + 2 ]; | |
}, | |
setZ: function ( index, z ) { | |
this.array[ index * this.itemSize + 2 ] = z; | |
return this; | |
}, | |
getW: function ( index ) { | |
return this.array[ index * this.itemSize + 3 ]; | |
}, | |
setW: function ( index, w ) { | |
this.array[ index * this.itemSize + 3 ] = w; | |
return this; | |
}, | |
setXY: function ( index, x, y ) { | |
index *= this.itemSize; | |
this.array[ index + 0 ] = x; | |
this.array[ index + 1 ] = y; | |
return this; | |
}, | |
setXYZ: function ( index, x, y, z ) { | |
index *= this.itemSize; | |
this.array[ index + 0 ] = x; | |
this.array[ index + 1 ] = y; | |
this.array[ index + 2 ] = z; | |
return this; | |
}, | |
setXYZW: function ( index, x, y, z, w ) { | |
index *= this.itemSize; | |
this.array[ index + 0 ] = x; | |
this.array[ index + 1 ] = y; | |
this.array[ index + 2 ] = z; | |
this.array[ index + 3 ] = w; | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
} | |
}; | |
// | |
THREE.Int8Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Int8Array( array ), itemSize ); | |
}; | |
THREE.Uint8Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Uint8Array( array ), itemSize ); | |
}; | |
THREE.Uint8ClampedAttribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Uint8ClampedArray( array ), itemSize ); | |
}; | |
THREE.Int16Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Int16Array( array ), itemSize ); | |
}; | |
THREE.Uint16Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Uint16Array( array ), itemSize ); | |
}; | |
THREE.Int32Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Int32Array( array ), itemSize ); | |
}; | |
THREE.Uint32Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Uint32Array( array ), itemSize ); | |
}; | |
THREE.Float32Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Float32Array( array ), itemSize ); | |
}; | |
THREE.Float64Attribute = function ( array, itemSize ) { | |
return new THREE.BufferAttribute( new Float64Array( array ), itemSize ); | |
}; | |
// Deprecated | |
THREE.DynamicBufferAttribute = function ( array, itemSize ) { | |
console.warn( 'THREE.DynamicBufferAttribute has been removed. Use new THREE.BufferAttribute().setDynamic( true ) instead.' ); | |
return new THREE.BufferAttribute( array, itemSize ).setDynamic( true ); | |
}; | |
// File:src/core/InstancedBufferAttribute.js | |
/** | |
* @author benaadams / https://twitter.com/ben_a_adams | |
*/ | |
THREE.InstancedBufferAttribute = function ( array, itemSize, meshPerAttribute ) { | |
THREE.BufferAttribute.call( this, array, itemSize ); | |
this.meshPerAttribute = meshPerAttribute || 1; | |
}; | |
THREE.InstancedBufferAttribute.prototype = Object.create( THREE.BufferAttribute.prototype ); | |
THREE.InstancedBufferAttribute.prototype.constructor = THREE.InstancedBufferAttribute; | |
THREE.InstancedBufferAttribute.prototype.copy = function ( source ) { | |
THREE.BufferAttribute.prototype.copy.call( this, source ); | |
this.meshPerAttribute = source.meshPerAttribute; | |
return this; | |
}; | |
// File:src/core/InterleavedBuffer.js | |
/** | |
* @author benaadams / https://twitter.com/ben_a_adams | |
*/ | |
THREE.InterleavedBuffer = function ( array, stride ) { | |
this.uuid = THREE.Math.generateUUID(); | |
this.array = array; | |
this.stride = stride; | |
this.dynamic = false; | |
this.updateRange = { offset: 0, count: - 1 }; | |
this.version = 0; | |
}; | |
THREE.InterleavedBuffer.prototype = { | |
constructor: THREE.InterleavedBuffer, | |
get length () { | |
return this.array.length; | |
}, | |
get count () { | |
return this.array.length / this.stride; | |
}, | |
set needsUpdate( value ) { | |
if ( value === true ) this.version ++; | |
}, | |
setDynamic: function ( value ) { | |
this.dynamic = value; | |
return this; | |
}, | |
copy: function ( source ) { | |
this.array = new source.array.constructor( source.array ); | |
this.stride = source.stride; | |
this.dynamic = source.dynamic; | |
return this; | |
}, | |
copyAt: function ( index1, attribute, index2 ) { | |
index1 *= this.stride; | |
index2 *= attribute.stride; | |
for ( var i = 0, l = this.stride; i < l; i ++ ) { | |
this.array[ index1 + i ] = attribute.array[ index2 + i ]; | |
} | |
return this; | |
}, | |
set: function ( value, offset ) { | |
if ( offset === undefined ) offset = 0; | |
this.array.set( value, offset ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
} | |
}; | |
// File:src/core/InstancedInterleavedBuffer.js | |
/** | |
* @author benaadams / https://twitter.com/ben_a_adams | |
*/ | |
THREE.InstancedInterleavedBuffer = function ( array, stride, meshPerAttribute ) { | |
THREE.InterleavedBuffer.call( this, array, stride ); | |
this.meshPerAttribute = meshPerAttribute || 1; | |
}; | |
THREE.InstancedInterleavedBuffer.prototype = Object.create( THREE.InterleavedBuffer.prototype ); | |
THREE.InstancedInterleavedBuffer.prototype.constructor = THREE.InstancedInterleavedBuffer; | |
THREE.InstancedInterleavedBuffer.prototype.copy = function ( source ) { | |
THREE.InterleavedBuffer.prototype.copy.call( this, source ); | |
this.meshPerAttribute = source.meshPerAttribute; | |
return this; | |
}; | |
// File:src/core/InterleavedBufferAttribute.js | |
/** | |
* @author benaadams / https://twitter.com/ben_a_adams | |
*/ | |
THREE.InterleavedBufferAttribute = function ( interleavedBuffer, itemSize, offset ) { | |
this.uuid = THREE.Math.generateUUID(); | |
this.data = interleavedBuffer; | |
this.itemSize = itemSize; | |
this.offset = offset; | |
}; | |
THREE.InterleavedBufferAttribute.prototype = { | |
constructor: THREE.InterleavedBufferAttribute, | |
get length() { | |
console.warn( 'THREE.BufferAttribute: .length has been deprecated. Please use .count.' ); | |
return this.array.length; | |
}, | |
get count() { | |
return this.data.count; | |
}, | |
setX: function ( index, x ) { | |
this.data.array[ index * this.data.stride + this.offset ] = x; | |
return this; | |
}, | |
setY: function ( index, y ) { | |
this.data.array[ index * this.data.stride + this.offset + 1 ] = y; | |
return this; | |
}, | |
setZ: function ( index, z ) { | |
this.data.array[ index * this.data.stride + this.offset + 2 ] = z; | |
return this; | |
}, | |
setW: function ( index, w ) { | |
this.data.array[ index * this.data.stride + this.offset + 3 ] = w; | |
return this; | |
}, | |
getX: function ( index ) { | |
return this.data.array[ index * this.data.stride + this.offset ]; | |
}, | |
getY: function ( index ) { | |
return this.data.array[ index * this.data.stride + this.offset + 1 ]; | |
}, | |
getZ: function ( index ) { | |
return this.data.array[ index * this.data.stride + this.offset + 2 ]; | |
}, | |
getW: function ( index ) { | |
return this.data.array[ index * this.data.stride + this.offset + 3 ]; | |
}, | |
setXY: function ( index, x, y ) { | |
index = index * this.data.stride + this.offset; | |
this.data.array[ index + 0 ] = x; | |
this.data.array[ index + 1 ] = y; | |
return this; | |
}, | |
setXYZ: function ( index, x, y, z ) { | |
index = index * this.data.stride + this.offset; | |
this.data.array[ index + 0 ] = x; | |
this.data.array[ index + 1 ] = y; | |
this.data.array[ index + 2 ] = z; | |
return this; | |
}, | |
setXYZW: function ( index, x, y, z, w ) { | |
index = index * this.data.stride + this.offset; | |
this.data.array[ index + 0 ] = x; | |
this.data.array[ index + 1 ] = y; | |
this.data.array[ index + 2 ] = z; | |
this.data.array[ index + 3 ] = w; | |
return this; | |
} | |
}; | |
// File:src/core/Geometry.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author kile / http://kile.stravaganza.org/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author zz85 / http://www.lab4games.net/zz85/blog | |
* @author bhouston / http://clara.io | |
*/ | |
THREE.Geometry = function () { | |
Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.type = 'Geometry'; | |
this.vertices = []; | |
this.colors = []; | |
this.faces = []; | |
this.faceVertexUvs = [ [] ]; | |
this.morphTargets = []; | |
this.morphNormals = []; | |
this.skinWeights = []; | |
this.skinIndices = []; | |
this.lineDistances = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
// update flags | |
this.verticesNeedUpdate = false; | |
this.elementsNeedUpdate = false; | |
this.uvsNeedUpdate = false; | |
this.normalsNeedUpdate = false; | |
this.colorsNeedUpdate = false; | |
this.lineDistancesNeedUpdate = false; | |
this.groupsNeedUpdate = false; | |
}; | |
THREE.Geometry.prototype = { | |
constructor: THREE.Geometry, | |
applyMatrix: function ( matrix ) { | |
var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); | |
for ( var i = 0, il = this.vertices.length; i < il; i ++ ) { | |
var vertex = this.vertices[ i ]; | |
vertex.applyMatrix4( matrix ); | |
} | |
for ( var i = 0, il = this.faces.length; i < il; i ++ ) { | |
var face = this.faces[ i ]; | |
face.normal.applyMatrix3( normalMatrix ).normalize(); | |
for ( var j = 0, jl = face.vertexNormals.length; j < jl; j ++ ) { | |
face.vertexNormals[ j ].applyMatrix3( normalMatrix ).normalize(); | |
} | |
} | |
if ( this.boundingBox !== null ) { | |
this.computeBoundingBox(); | |
} | |
if ( this.boundingSphere !== null ) { | |
this.computeBoundingSphere(); | |
} | |
this.verticesNeedUpdate = true; | |
this.normalsNeedUpdate = true; | |
}, | |
rotateX: function () { | |
// rotate geometry around world x-axis | |
var m1; | |
return function rotateX( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationX( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
rotateY: function () { | |
// rotate geometry around world y-axis | |
var m1; | |
return function rotateY( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationY( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
rotateZ: function () { | |
// rotate geometry around world z-axis | |
var m1; | |
return function rotateZ( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationZ( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
translate: function () { | |
// translate geometry | |
var m1; | |
return function translate( x, y, z ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeTranslation( x, y, z ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
scale: function () { | |
// scale geometry | |
var m1; | |
return function scale( x, y, z ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeScale( x, y, z ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
lookAt: function () { | |
var obj; | |
return function lookAt( vector ) { | |
if ( obj === undefined ) obj = new THREE.Object3D(); | |
obj.lookAt( vector ); | |
obj.updateMatrix(); | |
this.applyMatrix( obj.matrix ); | |
}; | |
}(), | |
fromBufferGeometry: function ( geometry ) { | |
var scope = this; | |
var indices = geometry.index !== null ? geometry.index.array : undefined; | |
var attributes = geometry.attributes; | |
var positions = attributes.position.array; | |
var normals = attributes.normal !== undefined ? attributes.normal.array : undefined; | |
var colors = attributes.color !== undefined ? attributes.color.array : undefined; | |
var uvs = attributes.uv !== undefined ? attributes.uv.array : undefined; | |
var uvs2 = attributes.uv2 !== undefined ? attributes.uv2.array : undefined; | |
if ( uvs2 !== undefined ) this.faceVertexUvs[ 1 ] = []; | |
var tempNormals = []; | |
var tempUVs = []; | |
var tempUVs2 = []; | |
for ( var i = 0, j = 0; i < positions.length; i += 3, j += 2 ) { | |
scope.vertices.push( new THREE.Vector3( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ) ); | |
if ( normals !== undefined ) { | |
tempNormals.push( new THREE.Vector3( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ) ); | |
} | |
if ( colors !== undefined ) { | |
scope.colors.push( new THREE.Color( colors[ i ], colors[ i + 1 ], colors[ i + 2 ] ) ); | |
} | |
if ( uvs !== undefined ) { | |
tempUVs.push( new THREE.Vector2( uvs[ j ], uvs[ j + 1 ] ) ); | |
} | |
if ( uvs2 !== undefined ) { | |
tempUVs2.push( new THREE.Vector2( uvs2[ j ], uvs2[ j + 1 ] ) ); | |
} | |
} | |
function addFace( a, b, c ) { | |
var vertexNormals = normals !== undefined ? [ tempNormals[ a ].clone(), tempNormals[ b ].clone(), tempNormals[ c ].clone() ] : []; | |
var vertexColors = colors !== undefined ? [ scope.colors[ a ].clone(), scope.colors[ b ].clone(), scope.colors[ c ].clone() ] : []; | |
var face = new THREE.Face3( a, b, c, vertexNormals, vertexColors ); | |
scope.faces.push( face ); | |
if ( uvs !== undefined ) { | |
scope.faceVertexUvs[ 0 ].push( [ tempUVs[ a ].clone(), tempUVs[ b ].clone(), tempUVs[ c ].clone() ] ); | |
} | |
if ( uvs2 !== undefined ) { | |
scope.faceVertexUvs[ 1 ].push( [ tempUVs2[ a ].clone(), tempUVs2[ b ].clone(), tempUVs2[ c ].clone() ] ); | |
} | |
} | |
if ( indices !== undefined ) { | |
var groups = geometry.groups; | |
if ( groups.length > 0 ) { | |
for ( var i = 0; i < groups.length; i ++ ) { | |
var group = groups[ i ]; | |
var start = group.start; | |
var count = group.count; | |
for ( var j = start, jl = start + count; j < jl; j += 3 ) { | |
addFace( indices[ j ], indices[ j + 1 ], indices[ j + 2 ] ); | |
} | |
} | |
} else { | |
for ( var i = 0; i < indices.length; i += 3 ) { | |
addFace( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); | |
} | |
} | |
} else { | |
for ( var i = 0; i < positions.length / 3; i += 3 ) { | |
addFace( i, i + 1, i + 2 ); | |
} | |
} | |
this.computeFaceNormals(); | |
if ( geometry.boundingBox !== null ) { | |
this.boundingBox = geometry.boundingBox.clone(); | |
} | |
if ( geometry.boundingSphere !== null ) { | |
this.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
return this; | |
}, | |
center: function () { | |
this.computeBoundingBox(); | |
var offset = this.boundingBox.center().negate(); | |
this.translate( offset.x, offset.y, offset.z ); | |
return offset; | |
}, | |
normalize: function () { | |
this.computeBoundingSphere(); | |
var center = this.boundingSphere.center; | |
var radius = this.boundingSphere.radius; | |
var s = radius === 0 ? 1 : 1.0 / radius; | |
var matrix = new THREE.Matrix4(); | |
matrix.set( | |
s, 0, 0, - s * center.x, | |
0, s, 0, - s * center.y, | |
0, 0, s, - s * center.z, | |
0, 0, 0, 1 | |
); | |
this.applyMatrix( matrix ); | |
return this; | |
}, | |
computeFaceNormals: function () { | |
var cb = new THREE.Vector3(), ab = new THREE.Vector3(); | |
for ( var f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
var face = this.faces[ f ]; | |
var vA = this.vertices[ face.a ]; | |
var vB = this.vertices[ face.b ]; | |
var vC = this.vertices[ face.c ]; | |
cb.subVectors( vC, vB ); | |
ab.subVectors( vA, vB ); | |
cb.cross( ab ); | |
cb.normalize(); | |
face.normal.copy( cb ); | |
} | |
}, | |
computeVertexNormals: function ( areaWeighted ) { | |
if ( areaWeighted === undefined ) areaWeighted = true; | |
var v, vl, f, fl, face, vertices; | |
vertices = new Array( this.vertices.length ); | |
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
vertices[ v ] = new THREE.Vector3(); | |
} | |
if ( areaWeighted ) { | |
// vertex normals weighted by triangle areas | |
// http://www.iquilezles.org/www/articles/normals/normals.htm | |
var vA, vB, vC; | |
var cb = new THREE.Vector3(), ab = new THREE.Vector3(); | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
vA = this.vertices[ face.a ]; | |
vB = this.vertices[ face.b ]; | |
vC = this.vertices[ face.c ]; | |
cb.subVectors( vC, vB ); | |
ab.subVectors( vA, vB ); | |
cb.cross( ab ); | |
vertices[ face.a ].add( cb ); | |
vertices[ face.b ].add( cb ); | |
vertices[ face.c ].add( cb ); | |
} | |
} else { | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
vertices[ face.a ].add( face.normal ); | |
vertices[ face.b ].add( face.normal ); | |
vertices[ face.c ].add( face.normal ); | |
} | |
} | |
for ( v = 0, vl = this.vertices.length; v < vl; v ++ ) { | |
vertices[ v ].normalize(); | |
} | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
var vertexNormals = face.vertexNormals; | |
if ( vertexNormals.length === 3 ) { | |
vertexNormals[ 0 ].copy( vertices[ face.a ] ); | |
vertexNormals[ 1 ].copy( vertices[ face.b ] ); | |
vertexNormals[ 2 ].copy( vertices[ face.c ] ); | |
} else { | |
vertexNormals[ 0 ] = vertices[ face.a ].clone(); | |
vertexNormals[ 1 ] = vertices[ face.b ].clone(); | |
vertexNormals[ 2 ] = vertices[ face.c ].clone(); | |
} | |
} | |
if ( this.faces.length > 0 ) { | |
this.normalsNeedUpdate = true; | |
} | |
}, | |
computeMorphNormals: function () { | |
var i, il, f, fl, face; | |
// save original normals | |
// - create temp variables on first access | |
// otherwise just copy (for faster repeated calls) | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
if ( ! face.__originalFaceNormal ) { | |
face.__originalFaceNormal = face.normal.clone(); | |
} else { | |
face.__originalFaceNormal.copy( face.normal ); | |
} | |
if ( ! face.__originalVertexNormals ) face.__originalVertexNormals = []; | |
for ( i = 0, il = face.vertexNormals.length; i < il; i ++ ) { | |
if ( ! face.__originalVertexNormals[ i ] ) { | |
face.__originalVertexNormals[ i ] = face.vertexNormals[ i ].clone(); | |
} else { | |
face.__originalVertexNormals[ i ].copy( face.vertexNormals[ i ] ); | |
} | |
} | |
} | |
// use temp geometry to compute face and vertex normals for each morph | |
var tmpGeo = new THREE.Geometry(); | |
tmpGeo.faces = this.faces; | |
for ( i = 0, il = this.morphTargets.length; i < il; i ++ ) { | |
// create on first access | |
if ( ! this.morphNormals[ i ] ) { | |
this.morphNormals[ i ] = {}; | |
this.morphNormals[ i ].faceNormals = []; | |
this.morphNormals[ i ].vertexNormals = []; | |
var dstNormalsFace = this.morphNormals[ i ].faceNormals; | |
var dstNormalsVertex = this.morphNormals[ i ].vertexNormals; | |
var faceNormal, vertexNormals; | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
faceNormal = new THREE.Vector3(); | |
vertexNormals = { a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() }; | |
dstNormalsFace.push( faceNormal ); | |
dstNormalsVertex.push( vertexNormals ); | |
} | |
} | |
var morphNormals = this.morphNormals[ i ]; | |
// set vertices to morph target | |
tmpGeo.vertices = this.morphTargets[ i ].vertices; | |
// compute morph normals | |
tmpGeo.computeFaceNormals(); | |
tmpGeo.computeVertexNormals(); | |
// store morph normals | |
var faceNormal, vertexNormals; | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
faceNormal = morphNormals.faceNormals[ f ]; | |
vertexNormals = morphNormals.vertexNormals[ f ]; | |
faceNormal.copy( face.normal ); | |
vertexNormals.a.copy( face.vertexNormals[ 0 ] ); | |
vertexNormals.b.copy( face.vertexNormals[ 1 ] ); | |
vertexNormals.c.copy( face.vertexNormals[ 2 ] ); | |
} | |
} | |
// restore original normals | |
for ( f = 0, fl = this.faces.length; f < fl; f ++ ) { | |
face = this.faces[ f ]; | |
face.normal = face.__originalFaceNormal; | |
face.vertexNormals = face.__originalVertexNormals; | |
} | |
}, | |
computeTangents: function () { | |
console.warn( 'THREE.Geometry: .computeTangents() has been removed.' ); | |
}, | |
computeLineDistances: function () { | |
var d = 0; | |
var vertices = this.vertices; | |
for ( var i = 0, il = vertices.length; i < il; i ++ ) { | |
if ( i > 0 ) { | |
d += vertices[ i ].distanceTo( vertices[ i - 1 ] ); | |
} | |
this.lineDistances[ i ] = d; | |
} | |
}, | |
computeBoundingBox: function () { | |
if ( this.boundingBox === null ) { | |
this.boundingBox = new THREE.Box3(); | |
} | |
this.boundingBox.setFromPoints( this.vertices ); | |
}, | |
computeBoundingSphere: function () { | |
if ( this.boundingSphere === null ) { | |
this.boundingSphere = new THREE.Sphere(); | |
} | |
this.boundingSphere.setFromPoints( this.vertices ); | |
}, | |
merge: function ( geometry, matrix, materialIndexOffset ) { | |
if ( geometry instanceof THREE.Geometry === false ) { | |
console.error( 'THREE.Geometry.merge(): geometry not an instance of THREE.Geometry.', geometry ); | |
return; | |
} | |
var normalMatrix, | |
vertexOffset = this.vertices.length, | |
vertices1 = this.vertices, | |
vertices2 = geometry.vertices, | |
faces1 = this.faces, | |
faces2 = geometry.faces, | |
uvs1 = this.faceVertexUvs[ 0 ], | |
uvs2 = geometry.faceVertexUvs[ 0 ]; | |
if ( materialIndexOffset === undefined ) materialIndexOffset = 0; | |
if ( matrix !== undefined ) { | |
normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); | |
} | |
// vertices | |
for ( var i = 0, il = vertices2.length; i < il; i ++ ) { | |
var vertex = vertices2[ i ]; | |
var vertexCopy = vertex.clone(); | |
if ( matrix !== undefined ) vertexCopy.applyMatrix4( matrix ); | |
vertices1.push( vertexCopy ); | |
} | |
// faces | |
for ( i = 0, il = faces2.length; i < il; i ++ ) { | |
var face = faces2[ i ], faceCopy, normal, color, | |
faceVertexNormals = face.vertexNormals, | |
faceVertexColors = face.vertexColors; | |
faceCopy = new THREE.Face3( face.a + vertexOffset, face.b + vertexOffset, face.c + vertexOffset ); | |
faceCopy.normal.copy( face.normal ); | |
if ( normalMatrix !== undefined ) { | |
faceCopy.normal.applyMatrix3( normalMatrix ).normalize(); | |
} | |
for ( var j = 0, jl = faceVertexNormals.length; j < jl; j ++ ) { | |
normal = faceVertexNormals[ j ].clone(); | |
if ( normalMatrix !== undefined ) { | |
normal.applyMatrix3( normalMatrix ).normalize(); | |
} | |
faceCopy.vertexNormals.push( normal ); | |
} | |
faceCopy.color.copy( face.color ); | |
for ( var j = 0, jl = faceVertexColors.length; j < jl; j ++ ) { | |
color = faceVertexColors[ j ]; | |
faceCopy.vertexColors.push( color.clone() ); | |
} | |
faceCopy.materialIndex = face.materialIndex + materialIndexOffset; | |
faces1.push( faceCopy ); | |
} | |
// uvs | |
for ( i = 0, il = uvs2.length; i < il; i ++ ) { | |
var uv = uvs2[ i ], uvCopy = []; | |
if ( uv === undefined ) { | |
continue; | |
} | |
for ( var j = 0, jl = uv.length; j < jl; j ++ ) { | |
uvCopy.push( uv[ j ].clone() ); | |
} | |
uvs1.push( uvCopy ); | |
} | |
}, | |
mergeMesh: function ( mesh ) { | |
if ( mesh instanceof THREE.Mesh === false ) { | |
console.error( 'THREE.Geometry.mergeMesh(): mesh not an instance of THREE.Mesh.', mesh ); | |
return; | |
} | |
mesh.matrixAutoUpdate && mesh.updateMatrix(); | |
this.merge( mesh.geometry, mesh.matrix ); | |
}, | |
/* | |
* Checks for duplicate vertices with hashmap. | |
* Duplicated vertices are removed | |
* and faces' vertices are updated. | |
*/ | |
mergeVertices: function () { | |
var verticesMap = {}; // Hashmap for looking up vertices by position coordinates (and making sure they are unique) | |
var unique = [], changes = []; | |
var v, key; | |
var precisionPoints = 4; // number of decimal points, e.g. 4 for epsilon of 0.0001 | |
var precision = Math.pow( 10, precisionPoints ); | |
var i, il, face; | |
var indices, j, jl; | |
for ( i = 0, il = this.vertices.length; i < il; i ++ ) { | |
v = this.vertices[ i ]; | |
key = Math.round( v.x * precision ) + '_' + Math.round( v.y * precision ) + '_' + Math.round( v.z * precision ); | |
if ( verticesMap[ key ] === undefined ) { | |
verticesMap[ key ] = i; | |
unique.push( this.vertices[ i ] ); | |
changes[ i ] = unique.length - 1; | |
} else { | |
//console.log('Duplicate vertex found. ', i, ' could be using ', verticesMap[key]); | |
changes[ i ] = changes[ verticesMap[ key ] ]; | |
} | |
} | |
// if faces are completely degenerate after merging vertices, we | |
// have to remove them from the geometry. | |
var faceIndicesToRemove = []; | |
for ( i = 0, il = this.faces.length; i < il; i ++ ) { | |
face = this.faces[ i ]; | |
face.a = changes[ face.a ]; | |
face.b = changes[ face.b ]; | |
face.c = changes[ face.c ]; | |
indices = [ face.a, face.b, face.c ]; | |
var dupIndex = - 1; | |
// if any duplicate vertices are found in a Face3 | |
// we have to remove the face as nothing can be saved | |
for ( var n = 0; n < 3; n ++ ) { | |
if ( indices[ n ] === indices[ ( n + 1 ) % 3 ] ) { | |
dupIndex = n; | |
faceIndicesToRemove.push( i ); | |
break; | |
} | |
} | |
} | |
for ( i = faceIndicesToRemove.length - 1; i >= 0; i -- ) { | |
var idx = faceIndicesToRemove[ i ]; | |
this.faces.splice( idx, 1 ); | |
for ( j = 0, jl = this.faceVertexUvs.length; j < jl; j ++ ) { | |
this.faceVertexUvs[ j ].splice( idx, 1 ); | |
} | |
} | |
// Use unique set of vertices | |
var diff = this.vertices.length - unique.length; | |
this.vertices = unique; | |
return diff; | |
}, | |
sortFacesByMaterialIndex: function () { | |
var faces = this.faces; | |
var length = faces.length; | |
// tag faces | |
for ( var i = 0; i < length; i ++ ) { | |
faces[ i ]._id = i; | |
} | |
// sort faces | |
function materialIndexSort( a, b ) { | |
return a.materialIndex - b.materialIndex; | |
} | |
faces.sort( materialIndexSort ); | |
// sort uvs | |
var uvs1 = this.faceVertexUvs[ 0 ]; | |
var uvs2 = this.faceVertexUvs[ 1 ]; | |
var newUvs1, newUvs2; | |
if ( uvs1 && uvs1.length === length ) newUvs1 = []; | |
if ( uvs2 && uvs2.length === length ) newUvs2 = []; | |
for ( var i = 0; i < length; i ++ ) { | |
var id = faces[ i ]._id; | |
if ( newUvs1 ) newUvs1.push( uvs1[ id ] ); | |
if ( newUvs2 ) newUvs2.push( uvs2[ id ] ); | |
} | |
if ( newUvs1 ) this.faceVertexUvs[ 0 ] = newUvs1; | |
if ( newUvs2 ) this.faceVertexUvs[ 1 ] = newUvs2; | |
}, | |
toJSON: function () { | |
var data = { | |
metadata: { | |
version: 4.4, | |
type: 'Geometry', | |
generator: 'Geometry.toJSON' | |
} | |
}; | |
// standard Geometry serialization | |
data.uuid = this.uuid; | |
data.type = this.type; | |
if ( this.name !== '' ) data.name = this.name; | |
if ( this.parameters !== undefined ) { | |
var parameters = this.parameters; | |
for ( var key in parameters ) { | |
if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; | |
} | |
return data; | |
} | |
var vertices = []; | |
for ( var i = 0; i < this.vertices.length; i ++ ) { | |
var vertex = this.vertices[ i ]; | |
vertices.push( vertex.x, vertex.y, vertex.z ); | |
} | |
var faces = []; | |
var normals = []; | |
var normalsHash = {}; | |
var colors = []; | |
var colorsHash = {}; | |
var uvs = []; | |
var uvsHash = {}; | |
for ( var i = 0; i < this.faces.length; i ++ ) { | |
var face = this.faces[ i ]; | |
var hasMaterial = true; | |
var hasFaceUv = false; // deprecated | |
var hasFaceVertexUv = this.faceVertexUvs[ 0 ][ i ] !== undefined; | |
var hasFaceNormal = face.normal.length() > 0; | |
var hasFaceVertexNormal = face.vertexNormals.length > 0; | |
var hasFaceColor = face.color.r !== 1 || face.color.g !== 1 || face.color.b !== 1; | |
var hasFaceVertexColor = face.vertexColors.length > 0; | |
var faceType = 0; | |
faceType = setBit( faceType, 0, 0 ); // isQuad | |
faceType = setBit( faceType, 1, hasMaterial ); | |
faceType = setBit( faceType, 2, hasFaceUv ); | |
faceType = setBit( faceType, 3, hasFaceVertexUv ); | |
faceType = setBit( faceType, 4, hasFaceNormal ); | |
faceType = setBit( faceType, 5, hasFaceVertexNormal ); | |
faceType = setBit( faceType, 6, hasFaceColor ); | |
faceType = setBit( faceType, 7, hasFaceVertexColor ); | |
faces.push( faceType ); | |
faces.push( face.a, face.b, face.c ); | |
faces.push( face.materialIndex ); | |
if ( hasFaceVertexUv ) { | |
var faceVertexUvs = this.faceVertexUvs[ 0 ][ i ]; | |
faces.push( | |
getUvIndex( faceVertexUvs[ 0 ] ), | |
getUvIndex( faceVertexUvs[ 1 ] ), | |
getUvIndex( faceVertexUvs[ 2 ] ) | |
); | |
} | |
if ( hasFaceNormal ) { | |
faces.push( getNormalIndex( face.normal ) ); | |
} | |
if ( hasFaceVertexNormal ) { | |
var vertexNormals = face.vertexNormals; | |
faces.push( | |
getNormalIndex( vertexNormals[ 0 ] ), | |
getNormalIndex( vertexNormals[ 1 ] ), | |
getNormalIndex( vertexNormals[ 2 ] ) | |
); | |
} | |
if ( hasFaceColor ) { | |
faces.push( getColorIndex( face.color ) ); | |
} | |
if ( hasFaceVertexColor ) { | |
var vertexColors = face.vertexColors; | |
faces.push( | |
getColorIndex( vertexColors[ 0 ] ), | |
getColorIndex( vertexColors[ 1 ] ), | |
getColorIndex( vertexColors[ 2 ] ) | |
); | |
} | |
} | |
function setBit( value, position, enabled ) { | |
return enabled ? value | ( 1 << position ) : value & ( ~ ( 1 << position ) ); | |
} | |
function getNormalIndex( normal ) { | |
var hash = normal.x.toString() + normal.y.toString() + normal.z.toString(); | |
if ( normalsHash[ hash ] !== undefined ) { | |
return normalsHash[ hash ]; | |
} | |
normalsHash[ hash ] = normals.length / 3; | |
normals.push( normal.x, normal.y, normal.z ); | |
return normalsHash[ hash ]; | |
} | |
function getColorIndex( color ) { | |
var hash = color.r.toString() + color.g.toString() + color.b.toString(); | |
if ( colorsHash[ hash ] !== undefined ) { | |
return colorsHash[ hash ]; | |
} | |
colorsHash[ hash ] = colors.length; | |
colors.push( color.getHex() ); | |
return colorsHash[ hash ]; | |
} | |
function getUvIndex( uv ) { | |
var hash = uv.x.toString() + uv.y.toString(); | |
if ( uvsHash[ hash ] !== undefined ) { | |
return uvsHash[ hash ]; | |
} | |
uvsHash[ hash ] = uvs.length / 2; | |
uvs.push( uv.x, uv.y ); | |
return uvsHash[ hash ]; | |
} | |
data.data = {}; | |
data.data.vertices = vertices; | |
data.data.normals = normals; | |
if ( colors.length > 0 ) data.data.colors = colors; | |
if ( uvs.length > 0 ) data.data.uvs = [ uvs ]; // temporal backward compatibility | |
data.data.faces = faces; | |
return data; | |
}, | |
clone: function () { | |
/* | |
// Handle primitives | |
var parameters = this.parameters; | |
if ( parameters !== undefined ) { | |
var values = []; | |
for ( var key in parameters ) { | |
values.push( parameters[ key ] ); | |
} | |
var geometry = Object.create( this.constructor.prototype ); | |
this.constructor.apply( geometry, values ); | |
return geometry; | |
} | |
return new this.constructor().copy( this ); | |
*/ | |
return new THREE.Geometry().copy( this ); | |
}, | |
copy: function ( source ) { | |
this.vertices = []; | |
this.faces = []; | |
this.faceVertexUvs = [ [] ]; | |
var vertices = source.vertices; | |
for ( var i = 0, il = vertices.length; i < il; i ++ ) { | |
this.vertices.push( vertices[ i ].clone() ); | |
} | |
var faces = source.faces; | |
for ( var i = 0, il = faces.length; i < il; i ++ ) { | |
this.faces.push( faces[ i ].clone() ); | |
} | |
for ( var i = 0, il = source.faceVertexUvs.length; i < il; i ++ ) { | |
var faceVertexUvs = source.faceVertexUvs[ i ]; | |
if ( this.faceVertexUvs[ i ] === undefined ) { | |
this.faceVertexUvs[ i ] = []; | |
} | |
for ( var j = 0, jl = faceVertexUvs.length; j < jl; j ++ ) { | |
var uvs = faceVertexUvs[ j ], uvsCopy = []; | |
for ( var k = 0, kl = uvs.length; k < kl; k ++ ) { | |
var uv = uvs[ k ]; | |
uvsCopy.push( uv.clone() ); | |
} | |
this.faceVertexUvs[ i ].push( uvsCopy ); | |
} | |
} | |
return this; | |
}, | |
dispose: function () { | |
this.dispatchEvent( { type: 'dispose' } ); | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.Geometry.prototype ); | |
THREE.GeometryIdCount = 0; | |
// File:src/core/DirectGeometry.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.DirectGeometry = function () { | |
Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.type = 'DirectGeometry'; | |
this.indices = []; | |
this.vertices = []; | |
this.normals = []; | |
this.colors = []; | |
this.uvs = []; | |
this.uvs2 = []; | |
this.groups = []; | |
this.morphTargets = {}; | |
this.skinWeights = []; | |
this.skinIndices = []; | |
// this.lineDistances = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
// update flags | |
this.verticesNeedUpdate = false; | |
this.normalsNeedUpdate = false; | |
this.colorsNeedUpdate = false; | |
this.uvsNeedUpdate = false; | |
this.groupsNeedUpdate = false; | |
}; | |
THREE.DirectGeometry.prototype = { | |
constructor: THREE.DirectGeometry, | |
computeBoundingBox: THREE.Geometry.prototype.computeBoundingBox, | |
computeBoundingSphere: THREE.Geometry.prototype.computeBoundingSphere, | |
computeFaceNormals: function () { | |
console.warn( 'THREE.DirectGeometry: computeFaceNormals() is not a method of this type of geometry.' ); | |
}, | |
computeVertexNormals: function () { | |
console.warn( 'THREE.DirectGeometry: computeVertexNormals() is not a method of this type of geometry.' ); | |
}, | |
computeGroups: function ( geometry ) { | |
var group; | |
var groups = []; | |
var materialIndex; | |
var faces = geometry.faces; | |
for ( var i = 0; i < faces.length; i ++ ) { | |
var face = faces[ i ]; | |
// materials | |
if ( face.materialIndex !== materialIndex ) { | |
materialIndex = face.materialIndex; | |
if ( group !== undefined ) { | |
group.count = ( i * 3 ) - group.start; | |
groups.push( group ); | |
} | |
group = { | |
start: i * 3, | |
materialIndex: materialIndex | |
}; | |
} | |
} | |
if ( group !== undefined ) { | |
group.count = ( i * 3 ) - group.start; | |
groups.push( group ); | |
} | |
this.groups = groups; | |
}, | |
fromGeometry: function ( geometry ) { | |
var faces = geometry.faces; | |
var vertices = geometry.vertices; | |
var faceVertexUvs = geometry.faceVertexUvs; | |
var hasFaceVertexUv = faceVertexUvs[ 0 ] && faceVertexUvs[ 0 ].length > 0; | |
var hasFaceVertexUv2 = faceVertexUvs[ 1 ] && faceVertexUvs[ 1 ].length > 0; | |
// morphs | |
var morphTargets = geometry.morphTargets; | |
var morphTargetsLength = morphTargets.length; | |
var morphTargetsPosition; | |
if ( morphTargetsLength > 0 ) { | |
morphTargetsPosition = []; | |
for ( var i = 0; i < morphTargetsLength; i ++ ) { | |
morphTargetsPosition[ i ] = []; | |
} | |
this.morphTargets.position = morphTargetsPosition; | |
} | |
var morphNormals = geometry.morphNormals; | |
var morphNormalsLength = morphNormals.length; | |
var morphTargetsNormal; | |
if ( morphNormalsLength > 0 ) { | |
morphTargetsNormal = []; | |
for ( var i = 0; i < morphNormalsLength; i ++ ) { | |
morphTargetsNormal[ i ] = []; | |
} | |
this.morphTargets.normal = morphTargetsNormal; | |
} | |
// skins | |
var skinIndices = geometry.skinIndices; | |
var skinWeights = geometry.skinWeights; | |
var hasSkinIndices = skinIndices.length === vertices.length; | |
var hasSkinWeights = skinWeights.length === vertices.length; | |
// | |
for ( var i = 0; i < faces.length; i ++ ) { | |
var face = faces[ i ]; | |
this.vertices.push( vertices[ face.a ], vertices[ face.b ], vertices[ face.c ] ); | |
var vertexNormals = face.vertexNormals; | |
if ( vertexNormals.length === 3 ) { | |
this.normals.push( vertexNormals[ 0 ], vertexNormals[ 1 ], vertexNormals[ 2 ] ); | |
} else { | |
var normal = face.normal; | |
this.normals.push( normal, normal, normal ); | |
} | |
var vertexColors = face.vertexColors; | |
if ( vertexColors.length === 3 ) { | |
this.colors.push( vertexColors[ 0 ], vertexColors[ 1 ], vertexColors[ 2 ] ); | |
} else { | |
var color = face.color; | |
this.colors.push( color, color, color ); | |
} | |
if ( hasFaceVertexUv === true ) { | |
var vertexUvs = faceVertexUvs[ 0 ][ i ]; | |
if ( vertexUvs !== undefined ) { | |
this.uvs.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
} else { | |
console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv ', i ); | |
this.uvs.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); | |
} | |
} | |
if ( hasFaceVertexUv2 === true ) { | |
var vertexUvs = faceVertexUvs[ 1 ][ i ]; | |
if ( vertexUvs !== undefined ) { | |
this.uvs2.push( vertexUvs[ 0 ], vertexUvs[ 1 ], vertexUvs[ 2 ] ); | |
} else { | |
console.warn( 'THREE.DirectGeometry.fromGeometry(): Undefined vertexUv2 ', i ); | |
this.uvs2.push( new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ); | |
} | |
} | |
// morphs | |
for ( var j = 0; j < morphTargetsLength; j ++ ) { | |
var morphTarget = morphTargets[ j ].vertices; | |
morphTargetsPosition[ j ].push( morphTarget[ face.a ], morphTarget[ face.b ], morphTarget[ face.c ] ); | |
} | |
for ( var j = 0; j < morphNormalsLength; j ++ ) { | |
var morphNormal = morphNormals[ j ].vertexNormals[ i ]; | |
morphTargetsNormal[ j ].push( morphNormal.a, morphNormal.b, morphNormal.c ); | |
} | |
// skins | |
if ( hasSkinIndices ) { | |
this.skinIndices.push( skinIndices[ face.a ], skinIndices[ face.b ], skinIndices[ face.c ] ); | |
} | |
if ( hasSkinWeights ) { | |
this.skinWeights.push( skinWeights[ face.a ], skinWeights[ face.b ], skinWeights[ face.c ] ); | |
} | |
} | |
this.computeGroups( geometry ); | |
this.verticesNeedUpdate = geometry.verticesNeedUpdate; | |
this.normalsNeedUpdate = geometry.normalsNeedUpdate; | |
this.colorsNeedUpdate = geometry.colorsNeedUpdate; | |
this.uvsNeedUpdate = geometry.uvsNeedUpdate; | |
this.groupsNeedUpdate = geometry.groupsNeedUpdate; | |
return this; | |
}, | |
dispose: function () { | |
this.dispatchEvent( { type: 'dispose' } ); | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.DirectGeometry.prototype ); | |
// File:src/core/BufferGeometry.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.BufferGeometry = function () { | |
Object.defineProperty( this, 'id', { value: THREE.GeometryIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.type = 'BufferGeometry'; | |
this.index = null; | |
this.attributes = {}; | |
this.morphAttributes = {}; | |
this.groups = []; | |
this.boundingBox = null; | |
this.boundingSphere = null; | |
this.drawRange = { start: 0, count: Infinity }; | |
}; | |
THREE.BufferGeometry.prototype = { | |
constructor: THREE.BufferGeometry, | |
getIndex: function () { | |
return this.index; | |
}, | |
setIndex: function ( index ) { | |
this.index = index; | |
}, | |
addAttribute: function ( name, attribute ) { | |
if ( attribute instanceof THREE.BufferAttribute === false && attribute instanceof THREE.InterleavedBufferAttribute === false ) { | |
console.warn( 'THREE.BufferGeometry: .addAttribute() now expects ( name, attribute ).' ); | |
this.addAttribute( name, new THREE.BufferAttribute( arguments[ 1 ], arguments[ 2 ] ) ); | |
return; | |
} | |
if ( name === 'index' ) { | |
console.warn( 'THREE.BufferGeometry.addAttribute: Use .setIndex() for index attribute.' ); | |
this.setIndex( attribute ); | |
return; | |
} | |
this.attributes[ name ] = attribute; | |
return this; | |
}, | |
getAttribute: function ( name ) { | |
return this.attributes[ name ]; | |
}, | |
removeAttribute: function ( name ) { | |
delete this.attributes[ name ]; | |
return this; | |
}, | |
addGroup: function ( start, count, materialIndex ) { | |
this.groups.push( { | |
start: start, | |
count: count, | |
materialIndex: materialIndex !== undefined ? materialIndex : 0 | |
} ); | |
}, | |
clearGroups: function () { | |
this.groups = []; | |
}, | |
setDrawRange: function ( start, count ) { | |
this.drawRange.start = start; | |
this.drawRange.count = count; | |
}, | |
applyMatrix: function ( matrix ) { | |
var position = this.attributes.position; | |
if ( position !== undefined ) { | |
matrix.applyToVector3Array( position.array ); | |
position.needsUpdate = true; | |
} | |
var normal = this.attributes.normal; | |
if ( normal !== undefined ) { | |
var normalMatrix = new THREE.Matrix3().getNormalMatrix( matrix ); | |
normalMatrix.applyToVector3Array( normal.array ); | |
normal.needsUpdate = true; | |
} | |
if ( this.boundingBox !== null ) { | |
this.computeBoundingBox(); | |
} | |
if ( this.boundingSphere !== null ) { | |
this.computeBoundingSphere(); | |
} | |
}, | |
rotateX: function () { | |
// rotate geometry around world x-axis | |
var m1; | |
return function rotateX( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationX( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
rotateY: function () { | |
// rotate geometry around world y-axis | |
var m1; | |
return function rotateY( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationY( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
rotateZ: function () { | |
// rotate geometry around world z-axis | |
var m1; | |
return function rotateZ( angle ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeRotationZ( angle ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
translate: function () { | |
// translate geometry | |
var m1; | |
return function translate( x, y, z ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeTranslation( x, y, z ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
scale: function () { | |
// scale geometry | |
var m1; | |
return function scale( x, y, z ) { | |
if ( m1 === undefined ) m1 = new THREE.Matrix4(); | |
m1.makeScale( x, y, z ); | |
this.applyMatrix( m1 ); | |
return this; | |
}; | |
}(), | |
lookAt: function () { | |
var obj; | |
return function lookAt( vector ) { | |
if ( obj === undefined ) obj = new THREE.Object3D(); | |
obj.lookAt( vector ); | |
obj.updateMatrix(); | |
this.applyMatrix( obj.matrix ); | |
}; | |
}(), | |
center: function () { | |
this.computeBoundingBox(); | |
var offset = this.boundingBox.center().negate(); | |
this.translate( offset.x, offset.y, offset.z ); | |
return offset; | |
}, | |
setFromObject: function ( object ) { | |
// console.log( 'THREE.BufferGeometry.setFromObject(). Converting', object, this ); | |
var geometry = object.geometry; | |
if ( object instanceof THREE.Points || object instanceof THREE.Line ) { | |
var positions = new THREE.Float32Attribute( geometry.vertices.length * 3, 3 ); | |
var colors = new THREE.Float32Attribute( geometry.colors.length * 3, 3 ); | |
this.addAttribute( 'position', positions.copyVector3sArray( geometry.vertices ) ); | |
this.addAttribute( 'color', colors.copyColorsArray( geometry.colors ) ); | |
if ( geometry.lineDistances && geometry.lineDistances.length === geometry.vertices.length ) { | |
var lineDistances = new THREE.Float32Attribute( geometry.lineDistances.length, 1 ); | |
this.addAttribute( 'lineDistance', lineDistances.copyArray( geometry.lineDistances ) ); | |
} | |
if ( geometry.boundingSphere !== null ) { | |
this.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
if ( geometry.boundingBox !== null ) { | |
this.boundingBox = geometry.boundingBox.clone(); | |
} | |
} else if ( object instanceof THREE.Mesh ) { | |
if ( geometry instanceof THREE.Geometry ) { | |
this.fromGeometry( geometry ); | |
} | |
} | |
return this; | |
}, | |
updateFromObject: function ( object ) { | |
var geometry = object.geometry; | |
if ( object instanceof THREE.Mesh ) { | |
var direct = geometry.__directGeometry; | |
if ( direct === undefined ) { | |
return this.fromGeometry( geometry ); | |
} | |
direct.verticesNeedUpdate = geometry.verticesNeedUpdate; | |
direct.normalsNeedUpdate = geometry.normalsNeedUpdate; | |
direct.colorsNeedUpdate = geometry.colorsNeedUpdate; | |
direct.uvsNeedUpdate = geometry.uvsNeedUpdate; | |
direct.groupsNeedUpdate = geometry.groupsNeedUpdate; | |
geometry.verticesNeedUpdate = false; | |
geometry.normalsNeedUpdate = false; | |
geometry.colorsNeedUpdate = false; | |
geometry.uvsNeedUpdate = false; | |
geometry.groupsNeedUpdate = false; | |
geometry = direct; | |
} | |
if ( geometry.verticesNeedUpdate === true ) { | |
var attribute = this.attributes.position; | |
if ( attribute !== undefined ) { | |
attribute.copyVector3sArray( geometry.vertices ); | |
attribute.needsUpdate = true; | |
} | |
geometry.verticesNeedUpdate = false; | |
} | |
if ( geometry.normalsNeedUpdate === true ) { | |
var attribute = this.attributes.normal; | |
if ( attribute !== undefined ) { | |
attribute.copyVector3sArray( geometry.normals ); | |
attribute.needsUpdate = true; | |
} | |
geometry.normalsNeedUpdate = false; | |
} | |
if ( geometry.colorsNeedUpdate === true ) { | |
var attribute = this.attributes.color; | |
if ( attribute !== undefined ) { | |
attribute.copyColorsArray( geometry.colors ); | |
attribute.needsUpdate = true; | |
} | |
geometry.colorsNeedUpdate = false; | |
} | |
if ( geometry.uvsNeedUpdate ) { | |
var attribute = this.attributes.uv; | |
if ( attribute !== undefined ) { | |
attribute.copyVector2sArray( geometry.uvs ); | |
attribute.needsUpdate = true; | |
} | |
geometry.uvsNeedUpdate = false; | |
} | |
if ( geometry.lineDistancesNeedUpdate ) { | |
var attribute = this.attributes.lineDistance; | |
if ( attribute !== undefined ) { | |
attribute.copyArray( geometry.lineDistances ); | |
attribute.needsUpdate = true; | |
} | |
geometry.lineDistancesNeedUpdate = false; | |
} | |
if ( geometry.groupsNeedUpdate ) { | |
geometry.computeGroups( object.geometry ); | |
this.groups = geometry.groups; | |
geometry.groupsNeedUpdate = false; | |
} | |
return this; | |
}, | |
fromGeometry: function ( geometry ) { | |
geometry.__directGeometry = new THREE.DirectGeometry().fromGeometry( geometry ); | |
return this.fromDirectGeometry( geometry.__directGeometry ); | |
}, | |
fromDirectGeometry: function ( geometry ) { | |
var positions = new Float32Array( geometry.vertices.length * 3 ); | |
this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ).copyVector3sArray( geometry.vertices ) ); | |
if ( geometry.normals.length > 0 ) { | |
var normals = new Float32Array( geometry.normals.length * 3 ); | |
this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ).copyVector3sArray( geometry.normals ) ); | |
} | |
if ( geometry.colors.length > 0 ) { | |
var colors = new Float32Array( geometry.colors.length * 3 ); | |
this.addAttribute( 'color', new THREE.BufferAttribute( colors, 3 ).copyColorsArray( geometry.colors ) ); | |
} | |
if ( geometry.uvs.length > 0 ) { | |
var uvs = new Float32Array( geometry.uvs.length * 2 ); | |
this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ).copyVector2sArray( geometry.uvs ) ); | |
} | |
if ( geometry.uvs2.length > 0 ) { | |
var uvs2 = new Float32Array( geometry.uvs2.length * 2 ); | |
this.addAttribute( 'uv2', new THREE.BufferAttribute( uvs2, 2 ).copyVector2sArray( geometry.uvs2 ) ); | |
} | |
if ( geometry.indices.length > 0 ) { | |
var TypeArray = geometry.vertices.length > 65535 ? Uint32Array : Uint16Array; | |
var indices = new TypeArray( geometry.indices.length * 3 ); | |
this.setIndex( new THREE.BufferAttribute( indices, 1 ).copyIndicesArray( geometry.indices ) ); | |
} | |
// groups | |
this.groups = geometry.groups; | |
// morphs | |
for ( var name in geometry.morphTargets ) { | |
var array = []; | |
var morphTargets = geometry.morphTargets[ name ]; | |
for ( var i = 0, l = morphTargets.length; i < l; i ++ ) { | |
var morphTarget = morphTargets[ i ]; | |
var attribute = new THREE.Float32Attribute( morphTarget.length * 3, 3 ); | |
array.push( attribute.copyVector3sArray( morphTarget ) ); | |
} | |
this.morphAttributes[ name ] = array; | |
} | |
// skinning | |
if ( geometry.skinIndices.length > 0 ) { | |
var skinIndices = new THREE.Float32Attribute( geometry.skinIndices.length * 4, 4 ); | |
this.addAttribute( 'skinIndex', skinIndices.copyVector4sArray( geometry.skinIndices ) ); | |
} | |
if ( geometry.skinWeights.length > 0 ) { | |
var skinWeights = new THREE.Float32Attribute( geometry.skinWeights.length * 4, 4 ); | |
this.addAttribute( 'skinWeight', skinWeights.copyVector4sArray( geometry.skinWeights ) ); | |
} | |
// | |
if ( geometry.boundingSphere !== null ) { | |
this.boundingSphere = geometry.boundingSphere.clone(); | |
} | |
if ( geometry.boundingBox !== null ) { | |
this.boundingBox = geometry.boundingBox.clone(); | |
} | |
return this; | |
}, | |
computeBoundingBox: function () { | |
var vector = new THREE.Vector3(); | |
return function () { | |
if ( this.boundingBox === null ) { | |
this.boundingBox = new THREE.Box3(); | |
} | |
var positions = this.attributes.position.array; | |
if ( positions ) { | |
this.boundingBox.setFromArray( positions ); | |
} | |
if ( positions === undefined || positions.length === 0 ) { | |
this.boundingBox.min.set( 0, 0, 0 ); | |
this.boundingBox.max.set( 0, 0, 0 ); | |
} | |
if ( isNaN( this.boundingBox.min.x ) || isNaN( this.boundingBox.min.y ) || isNaN( this.boundingBox.min.z ) ) { | |
console.error( 'THREE.BufferGeometry.computeBoundingBox: Computed min/max have NaN values. The "position" attribute is likely to have NaN values.', this ); | |
} | |
}; | |
}(), | |
computeBoundingSphere: function () { | |
var box = new THREE.Box3(); | |
var vector = new THREE.Vector3(); | |
return function () { | |
if ( this.boundingSphere === null ) { | |
this.boundingSphere = new THREE.Sphere(); | |
} | |
var positions = this.attributes.position.array; | |
if ( positions ) { | |
var center = this.boundingSphere.center; | |
box.setFromArray( positions ); | |
box.center( center ); | |
// hoping to find a boundingSphere with a radius smaller than the | |
// boundingSphere of the boundingBox: sqrt(3) smaller in the best case | |
var maxRadiusSq = 0; | |
for ( var i = 0, il = positions.length; i < il; i += 3 ) { | |
vector.fromArray( positions, i ); | |
maxRadiusSq = Math.max( maxRadiusSq, center.distanceToSquared( vector ) ); | |
} | |
this.boundingSphere.radius = Math.sqrt( maxRadiusSq ); | |
if ( isNaN( this.boundingSphere.radius ) ) { | |
console.error( 'THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values.', this ); | |
} | |
} | |
}; | |
}(), | |
computeFaceNormals: function () { | |
// backwards compatibility | |
}, | |
computeVertexNormals: function () { | |
var index = this.index; | |
var attributes = this.attributes; | |
var groups = this.groups; | |
if ( attributes.position ) { | |
var positions = attributes.position.array; | |
if ( attributes.normal === undefined ) { | |
this.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( positions.length ), 3 ) ); | |
} else { | |
// reset existing normals to zero | |
var array = attributes.normal.array; | |
for ( var i = 0, il = array.length; i < il; i ++ ) { | |
array[ i ] = 0; | |
} | |
} | |
var normals = attributes.normal.array; | |
var vA, vB, vC, | |
pA = new THREE.Vector3(), | |
pB = new THREE.Vector3(), | |
pC = new THREE.Vector3(), | |
cb = new THREE.Vector3(), | |
ab = new THREE.Vector3(); | |
// indexed elements | |
if ( index ) { | |
var indices = index.array; | |
if ( groups.length === 0 ) { | |
this.addGroup( 0, indices.length ); | |
} | |
for ( var j = 0, jl = groups.length; j < jl; ++ j ) { | |
var group = groups[ j ]; | |
var start = group.start; | |
var count = group.count; | |
for ( var i = start, il = start + count; i < il; i += 3 ) { | |
vA = indices[ i + 0 ] * 3; | |
vB = indices[ i + 1 ] * 3; | |
vC = indices[ i + 2 ] * 3; | |
pA.fromArray( positions, vA ); | |
pB.fromArray( positions, vB ); | |
pC.fromArray( positions, vC ); | |
cb.subVectors( pC, pB ); | |
ab.subVectors( pA, pB ); | |
cb.cross( ab ); | |
normals[ vA ] += cb.x; | |
normals[ vA + 1 ] += cb.y; | |
normals[ vA + 2 ] += cb.z; | |
normals[ vB ] += cb.x; | |
normals[ vB + 1 ] += cb.y; | |
normals[ vB + 2 ] += cb.z; | |
normals[ vC ] += cb.x; | |
normals[ vC + 1 ] += cb.y; | |
normals[ vC + 2 ] += cb.z; | |
} | |
} | |
} else { | |
// non-indexed elements (unconnected triangle soup) | |
for ( var i = 0, il = positions.length; i < il; i += 9 ) { | |
pA.fromArray( positions, i ); | |
pB.fromArray( positions, i + 3 ); | |
pC.fromArray( positions, i + 6 ); | |
cb.subVectors( pC, pB ); | |
ab.subVectors( pA, pB ); | |
cb.cross( ab ); | |
normals[ i ] = cb.x; | |
normals[ i + 1 ] = cb.y; | |
normals[ i + 2 ] = cb.z; | |
normals[ i + 3 ] = cb.x; | |
normals[ i + 4 ] = cb.y; | |
normals[ i + 5 ] = cb.z; | |
normals[ i + 6 ] = cb.x; | |
normals[ i + 7 ] = cb.y; | |
normals[ i + 8 ] = cb.z; | |
} | |
} | |
this.normalizeNormals(); | |
attributes.normal.needsUpdate = true; | |
} | |
}, | |
merge: function ( geometry, offset ) { | |
if ( geometry instanceof THREE.BufferGeometry === false ) { | |
console.error( 'THREE.BufferGeometry.merge(): geometry not an instance of THREE.BufferGeometry.', geometry ); | |
return; | |
} | |
if ( offset === undefined ) offset = 0; | |
var attributes = this.attributes; | |
for ( var key in attributes ) { | |
if ( geometry.attributes[ key ] === undefined ) continue; | |
var attribute1 = attributes[ key ]; | |
var attributeArray1 = attribute1.array; | |
var attribute2 = geometry.attributes[ key ]; | |
var attributeArray2 = attribute2.array; | |
var attributeSize = attribute2.itemSize; | |
for ( var i = 0, j = attributeSize * offset; i < attributeArray2.length; i ++, j ++ ) { | |
attributeArray1[ j ] = attributeArray2[ i ]; | |
} | |
} | |
return this; | |
}, | |
normalizeNormals: function () { | |
var normals = this.attributes.normal.array; | |
var x, y, z, n; | |
for ( var i = 0, il = normals.length; i < il; i += 3 ) { | |
x = normals[ i ]; | |
y = normals[ i + 1 ]; | |
z = normals[ i + 2 ]; | |
n = 1.0 / Math.sqrt( x * x + y * y + z * z ); | |
normals[ i ] *= n; | |
normals[ i + 1 ] *= n; | |
normals[ i + 2 ] *= n; | |
} | |
}, | |
toNonIndexed: function () { | |
if ( this.index === null ) { | |
console.warn( 'THREE.BufferGeometry.toNonIndexed(): Geometry is already non-indexed.' ); | |
return this; | |
} | |
var geometry2 = new THREE.BufferGeometry(); | |
var indices = this.index.array; | |
var attributes = this.attributes; | |
for ( var name in attributes ) { | |
var attribute = attributes[ name ]; | |
var array = attribute.array; | |
var itemSize = attribute.itemSize; | |
var array2 = new array.constructor( indices.length * itemSize ); | |
var index = 0, index2 = 0; | |
for ( var i = 0, l = indices.length; i < l; i ++ ) { | |
index = indices[ i ] * itemSize; | |
for ( var j = 0; j < itemSize; j ++ ) { | |
array2[ index2 ++ ] = array[ index ++ ]; | |
} | |
} | |
geometry2.addAttribute( name, new THREE.BufferAttribute( array2, itemSize ) ); | |
} | |
return geometry2; | |
}, | |
toJSON: function () { | |
var data = { | |
metadata: { | |
version: 4.4, | |
type: 'BufferGeometry', | |
generator: 'BufferGeometry.toJSON' | |
} | |
}; | |
// standard BufferGeometry serialization | |
data.uuid = this.uuid; | |
data.type = this.type; | |
if ( this.name !== '' ) data.name = this.name; | |
if ( this.parameters !== undefined ) { | |
var parameters = this.parameters; | |
for ( var key in parameters ) { | |
if ( parameters[ key ] !== undefined ) data[ key ] = parameters[ key ]; | |
} | |
return data; | |
} | |
data.data = { attributes: {} }; | |
var index = this.index; | |
if ( index !== null ) { | |
var array = Array.prototype.slice.call( index.array ); | |
data.data.index = { | |
type: index.array.constructor.name, | |
array: array | |
}; | |
} | |
var attributes = this.attributes; | |
for ( var key in attributes ) { | |
var attribute = attributes[ key ]; | |
var array = Array.prototype.slice.call( attribute.array ); | |
data.data.attributes[ key ] = { | |
itemSize: attribute.itemSize, | |
type: attribute.array.constructor.name, | |
array: array | |
}; | |
} | |
var groups = this.groups; | |
if ( groups.length > 0 ) { | |
data.data.groups = JSON.parse( JSON.stringify( groups ) ); | |
} | |
var boundingSphere = this.boundingSphere; | |
if ( boundingSphere !== null ) { | |
data.data.boundingSphere = { | |
center: boundingSphere.center.toArray(), | |
radius: boundingSphere.radius | |
}; | |
} | |
return data; | |
}, | |
clone: function () { | |
/* | |
// Handle primitives | |
var parameters = this.parameters; | |
if ( parameters !== undefined ) { | |
var values = []; | |
for ( var key in parameters ) { | |
values.push( parameters[ key ] ); | |
} | |
var geometry = Object.create( this.constructor.prototype ); | |
this.constructor.apply( geometry, values ); | |
return geometry; | |
} | |
return new this.constructor().copy( this ); | |
*/ | |
return new THREE.BufferGeometry().copy( this ); | |
}, | |
copy: function ( source ) { | |
var index = source.index; | |
if ( index !== null ) { | |
this.setIndex( index.clone() ); | |
} | |
var attributes = source.attributes; | |
for ( var name in attributes ) { | |
var attribute = attributes[ name ]; | |
this.addAttribute( name, attribute.clone() ); | |
} | |
var groups = source.groups; | |
for ( var i = 0, l = groups.length; i < l; i ++ ) { | |
var group = groups[ i ]; | |
this.addGroup( group.start, group.count ); | |
} | |
return this; | |
}, | |
dispose: function () { | |
this.dispatchEvent( { type: 'dispose' } ); | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.BufferGeometry.prototype ); | |
THREE.BufferGeometry.MaxIndex = 65535; | |
// File:src/core/InstancedBufferGeometry.js | |
/** | |
* @author benaadams / https://twitter.com/ben_a_adams | |
*/ | |
THREE.InstancedBufferGeometry = function () { | |
THREE.BufferGeometry.call( this ); | |
this.type = 'InstancedBufferGeometry'; | |
this.maxInstancedCount = undefined; | |
}; | |
THREE.InstancedBufferGeometry.prototype = Object.create( THREE.BufferGeometry.prototype ); | |
THREE.InstancedBufferGeometry.prototype.constructor = THREE.InstancedBufferGeometry; | |
THREE.InstancedBufferGeometry.prototype.addGroup = function ( start, count, instances ) { | |
this.groups.push( { | |
start: start, | |
count: count, | |
instances: instances | |
} ); | |
}; | |
THREE.InstancedBufferGeometry.prototype.copy = function ( source ) { | |
var index = source.index; | |
if ( index !== null ) { | |
this.setIndex( index.clone() ); | |
} | |
var attributes = source.attributes; | |
for ( var name in attributes ) { | |
var attribute = attributes[ name ]; | |
this.addAttribute( name, attribute.clone() ); | |
} | |
var groups = source.groups; | |
for ( var i = 0, l = groups.length; i < l; i ++ ) { | |
var group = groups[ i ]; | |
this.addGroup( group.start, group.count, group.instances ); | |
} | |
return this; | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.InstancedBufferGeometry.prototype ); | |
// File:src/core/Uniform.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Uniform = function ( type, value ) { | |
this.type = type; | |
this.value = value; | |
this.dynamic = false; | |
}; | |
THREE.Uniform.prototype = { | |
constructor: THREE.Uniform, | |
onUpdate: function ( callback ) { | |
this.dynamic = true; | |
this.onUpdateCallback = callback; | |
return this; | |
} | |
}; | |
// File:src/animation/AnimationClip.js | |
/** | |
* | |
* Reusable set of Tracks that represent an animation. | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
*/ | |
THREE.AnimationClip = function ( name, duration, tracks ) { | |
this.name = name || THREE.Math.generateUUID(); | |
this.tracks = tracks; | |
this.duration = ( duration !== undefined ) ? duration : -1; | |
// this means it should figure out its duration by scanning the tracks | |
if ( this.duration < 0 ) { | |
this.resetDuration(); | |
} | |
// maybe only do these on demand, as doing them here could potentially slow down loading | |
// but leaving these here during development as this ensures a lot of testing of these functions | |
this.trim(); | |
this.optimize(); | |
}; | |
THREE.AnimationClip.prototype = { | |
constructor: THREE.AnimationClip, | |
resetDuration: function() { | |
var tracks = this.tracks, | |
duration = 0; | |
for ( var i = 0, n = tracks.length; i !== n; ++ i ) { | |
var track = this.tracks[ i ]; | |
duration = Math.max( | |
duration, track.times[ track.times.length - 1 ] ); | |
} | |
this.duration = duration; | |
}, | |
trim: function() { | |
for ( var i = 0; i < this.tracks.length; i ++ ) { | |
this.tracks[ i ].trim( 0, this.duration ); | |
} | |
return this; | |
}, | |
optimize: function() { | |
for ( var i = 0; i < this.tracks.length; i ++ ) { | |
this.tracks[ i ].optimize(); | |
} | |
return this; | |
} | |
}; | |
// Static methods: | |
Object.assign( THREE.AnimationClip, { | |
parse: function( json ) { | |
var tracks = [], | |
jsonTracks = json.tracks, | |
frameTime = 1.0 / ( json.fps || 1.0 ); | |
for ( var i = 0, n = jsonTracks.length; i !== n; ++ i ) { | |
tracks.push( THREE.KeyframeTrack.parse( jsonTracks[ i ] ).scale( frameTime ) ); | |
} | |
return new THREE.AnimationClip( json.name, json.duration, tracks ); | |
}, | |
toJSON: function( clip ) { | |
var tracks = [], | |
clipTracks = clip.tracks; | |
var json = { | |
'name': clip.name, | |
'duration': clip.duration, | |
'tracks': tracks | |
}; | |
for ( var i = 0, n = clipTracks.length; i !== n; ++ i ) { | |
tracks.push( THREE.KeyframeTrack.toJSON( clipTracks[ i ] ) ); | |
} | |
return json; | |
}, | |
CreateFromMorphTargetSequence: function( name, morphTargetSequence, fps ) { | |
var numMorphTargets = morphTargetSequence.length; | |
var tracks = []; | |
for ( var i = 0; i < numMorphTargets; i ++ ) { | |
var times = []; | |
var values = []; | |
times.push( | |
( i + numMorphTargets - 1 ) % numMorphTargets, | |
i, | |
( i + 1 ) % numMorphTargets ); | |
values.push( 0, 1, 0 ); | |
var order = THREE.AnimationUtils.getKeyframeOrder( times ); | |
times = THREE.AnimationUtils.sortedArray( times, 1, order ); | |
values = THREE.AnimationUtils.sortedArray( values, 1, order ); | |
// if there is a key at the first frame, duplicate it as the | |
// last frame as well for perfect loop. | |
if ( times[ 0 ] === 0 ) { | |
times.push( numMorphTargets ); | |
values.push( values[ 0 ] ); | |
} | |
tracks.push( | |
new THREE.NumberKeyframeTrack( | |
'.morphTargetInfluences[' + morphTargetSequence[ i ].name + ']', | |
times, values | |
).scale( 1.0 / fps ) ); | |
} | |
return new THREE.AnimationClip( name, -1, tracks ); | |
}, | |
findByName: function( clipArray, name ) { | |
for ( var i = 0; i < clipArray.length; i ++ ) { | |
if ( clipArray[ i ].name === name ) { | |
return clipArray[ i ]; | |
} | |
} | |
return null; | |
}, | |
CreateClipsFromMorphTargetSequences: function( morphTargets, fps ) { | |
var animationToMorphTargets = {}; | |
// tested with https://regex101.com/ on trick sequences | |
// such flamingo_flyA_003, flamingo_run1_003, crdeath0059 | |
var pattern = /^([\w-]*?)([\d]+)$/; | |
// sort morph target names into animation groups based | |
// patterns like Walk_001, Walk_002, Run_001, Run_002 | |
for ( var i = 0, il = morphTargets.length; i < il; i ++ ) { | |
var morphTarget = morphTargets[ i ]; | |
var parts = morphTarget.name.match( pattern ); | |
if ( parts && parts.length > 1 ) { | |
var name = parts[ 1 ]; | |
var animationMorphTargets = animationToMorphTargets[ name ]; | |
if ( ! animationMorphTargets ) { | |
animationToMorphTargets[ name ] = animationMorphTargets = []; | |
} | |
animationMorphTargets.push( morphTarget ); | |
} | |
} | |
var clips = []; | |
for ( var name in animationToMorphTargets ) { | |
clips.push( THREE.AnimationClip.CreateFromMorphTargetSequence( name, animationToMorphTargets[ name ], fps ) ); | |
} | |
return clips; | |
}, | |
// parse the animation.hierarchy format | |
parseAnimation: function( animation, bones, nodeName ) { | |
if ( ! animation ) { | |
console.error( " no animation in JSONLoader data" ); | |
return null; | |
} | |
var addNonemptyTrack = function( | |
trackType, trackName, animationKeys, propertyName, destTracks ) { | |
// only return track if there are actually keys. | |
if ( animationKeys.length !== 0 ) { | |
var times = []; | |
var values = []; | |
THREE.AnimationUtils.flattenJSON( | |
animationKeys, times, values, propertyName ); | |
// empty keys are filtered out, so check again | |
if ( times.length !== 0 ) { | |
destTracks.push( new trackType( trackName, times, values ) ); | |
} | |
} | |
}; | |
var tracks = []; | |
var clipName = animation.name || 'default'; | |
// automatic length determination in AnimationClip. | |
var duration = animation.length || -1; | |
var fps = animation.fps || 30; | |
var hierarchyTracks = animation.hierarchy || []; | |
for ( var h = 0; h < hierarchyTracks.length; h ++ ) { | |
var animationKeys = hierarchyTracks[ h ].keys; | |
// skip empty tracks | |
if ( ! animationKeys || animationKeys.length == 0 ) continue; | |
// process morph targets in a way exactly compatible | |
// with AnimationHandler.init( animation ) | |
if ( animationKeys[0].morphTargets ) { | |
// figure out all morph targets used in this track | |
var morphTargetNames = {}; | |
for ( var k = 0; k < animationKeys.length; k ++ ) { | |
if ( animationKeys[k].morphTargets ) { | |
for ( var m = 0; m < animationKeys[k].morphTargets.length; m ++ ) { | |
morphTargetNames[ animationKeys[k].morphTargets[m] ] = -1; | |
} | |
} | |
} | |
// create a track for each morph target with all zero | |
// morphTargetInfluences except for the keys in which | |
// the morphTarget is named. | |
for ( var morphTargetName in morphTargetNames ) { | |
var times = []; | |
var values = []; | |
for ( var m = 0; | |
m !== animationKeys[k].morphTargets.length; ++ m ) { | |
var animationKey = animationKeys[k]; | |
times.push( animationKey.time ); | |
values.push( ( animationKey.morphTarget === morphTargetName ) ? 1 : 0 ) | |
} | |
tracks.push( new THREE.NumberKeyframeTrack( | |
'.morphTargetInfluence[' + morphTargetName + ']', times, values ) ); | |
} | |
duration = morphTargetNames.length * ( fps || 1.0 ); | |
} else { | |
// ...assume skeletal animation | |
var boneName = '.bones[' + bones[ h ].name + ']'; | |
addNonemptyTrack( | |
THREE.VectorKeyframeTrack, boneName + '.position', | |
animationKeys, 'pos', tracks ); | |
addNonemptyTrack( | |
THREE.QuaternionKeyframeTrack, boneName + '.quaternion', | |
animationKeys, 'rot', tracks ); | |
addNonemptyTrack( | |
THREE.VectorKeyframeTrack, boneName + '.scale', | |
animationKeys, 'scl', tracks ); | |
} | |
} | |
if ( tracks.length === 0 ) { | |
return null; | |
} | |
var clip = new THREE.AnimationClip( clipName, duration, tracks ); | |
return clip; | |
} | |
} ); | |
// File:src/animation/AnimationMixer.js | |
/** | |
* | |
* Player for AnimationClips. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.AnimationMixer = function( root ) { | |
this._root = root; | |
this._initMemoryManager(); | |
this._accuIndex = 0; | |
this.time = 0; | |
this.timeScale = 1.0; | |
}; | |
THREE.AnimationMixer.prototype = { | |
constructor: THREE.AnimationMixer, | |
// return an action for a clip optionally using a custom root target | |
// object (this method allocates a lot of dynamic memory in case a | |
// previously unknown clip/root combination is specified) | |
clipAction: function( clip, optionalRoot ) { | |
var root = optionalRoot || this._root, | |
rootUuid = root.uuid, | |
clipName = ( typeof clip === 'string' ) ? clip : clip.name, | |
clipObject = ( clip !== clipName ) ? clip : null, | |
actionsForClip = this._actionsByClip[ clipName ], | |
prototypeAction; | |
if ( actionsForClip !== undefined ) { | |
var existingAction = | |
actionsForClip.actionByRoot[ rootUuid ]; | |
if ( existingAction !== undefined ) { | |
return existingAction; | |
} | |
// we know the clip, so we don't have to parse all | |
// the bindings again but can just copy | |
prototypeAction = actionsForClip.knownActions[ 0 ]; | |
// also, take the clip from the prototype action | |
clipObject = prototypeAction._clip; | |
if ( clip !== clipName && clip !== clipObject ) { | |
throw new Error( | |
"Different clips with the same name detected!" ); | |
} | |
} | |
// clip must be known when specified via string | |
if ( clipObject === null ) return null; | |
// allocate all resources required to run it | |
var newAction = new THREE. | |
AnimationMixer._Action( this, clipObject, optionalRoot ); | |
this._bindAction( newAction, prototypeAction ); | |
// and make the action known to the memory manager | |
this._addInactiveAction( newAction, clipName, rootUuid ); | |
return newAction; | |
}, | |
// get an existing action | |
existingAction: function( clip, optionalRoot ) { | |
var root = optionalRoot || this._root, | |
rootUuid = root.uuid, | |
clipName = ( typeof clip === 'string' ) ? clip : clip.name, | |
actionsForClip = this._actionsByClip[ clipName ]; | |
if ( actionsForClip !== undefined ) { | |
return actionsForClip.actionByRoot[ rootUuid ] || null; | |
} | |
return null; | |
}, | |
// deactivates all previously scheduled actions | |
stopAllAction: function() { | |
var actions = this._actions, | |
nActions = this._nActiveActions, | |
bindings = this._bindings, | |
nBindings = this._nActiveBindings; | |
this._nActiveActions = 0; | |
this._nActiveBindings = 0; | |
for ( var i = 0; i !== nActions; ++ i ) { | |
actions[ i ].reset(); | |
} | |
for ( var i = 0; i !== nBindings; ++ i ) { | |
bindings[ i ].useCount = 0; | |
} | |
return this; | |
}, | |
// advance the time and update apply the animation | |
update: function( deltaTime ) { | |
deltaTime *= this.timeScale; | |
var actions = this._actions, | |
nActions = this._nActiveActions, | |
time = this.time += deltaTime, | |
timeDirection = Math.sign( deltaTime ), | |
accuIndex = this._accuIndex ^= 1; | |
// run active actions | |
for ( var i = 0; i !== nActions; ++ i ) { | |
var action = actions[ i ]; | |
if ( action.enabled ) { | |
action._update( time, deltaTime, timeDirection, accuIndex ); | |
} | |
} | |
// update scene graph | |
var bindings = this._bindings, | |
nBindings = this._nActiveBindings; | |
for ( var i = 0; i !== nBindings; ++ i ) { | |
bindings[ i ].apply( accuIndex ); | |
} | |
return this; | |
}, | |
// return this mixer's root target object | |
getRoot: function() { | |
return this._root; | |
}, | |
// free all resources specific to a particular clip | |
uncacheClip: function( clip ) { | |
var actions = this._actions, | |
clipName = clip.name, | |
actionsByClip = this._actionsByClip, | |
actionsForClip = actionsByClip[ clipName ]; | |
if ( actionsForClip !== undefined ) { | |
// note: just calling _removeInactiveAction would mess up the | |
// iteration state and also require updating the state we can | |
// just throw away | |
var actionsToRemove = actionsForClip.knownActions; | |
for ( var i = 0, n = actionsToRemove.length; i !== n; ++ i ) { | |
var action = actionsToRemove[ i ]; | |
this._deactivateAction( action ); | |
var cacheIndex = action._cacheIndex, | |
lastInactiveAction = actions[ actions.length - 1 ]; | |
action._cacheIndex = null; | |
action._byClipCacheIndex = null; | |
lastInactiveAction._cacheIndex = cacheIndex; | |
actions[ cacheIndex ] = lastInactiveAction; | |
actions.pop(); | |
this._removeInactiveBindingsForAction( action ); | |
} | |
delete actionsByClip[ clipName ]; | |
} | |
}, | |
// free all resources specific to a particular root target object | |
uncacheRoot: function( root ) { | |
var rootUuid = root.uuid, | |
actionsByClip = this._actionsByClip; | |
for ( var clipName in actionsByClip ) { | |
var actionByRoot = actionsByClip[ clipName ].actionByRoot, | |
action = actionByRoot[ rootUuid ]; | |
if ( action !== undefined ) { | |
this._deactivateAction( action ); | |
this._removeInactiveAction( action ); | |
} | |
} | |
var bindingsByRoot = this._bindingsByRootAndName, | |
bindingByName = bindingsByRoot[ rootUuid ]; | |
if ( bindingByName !== undefined ) { | |
for ( var trackName in bindingByName ) { | |
var binding = bindingByName[ trackName ]; | |
binding.restoreOriginalState(); | |
this._removeInactiveBinding( binding ); | |
} | |
} | |
}, | |
// remove a targeted clip from the cache | |
uncacheAction: function( clip, optionalRoot ) { | |
var action = this.existingAction( clip, optionalRoot ); | |
if ( action !== null ) { | |
this._deactivateAction( action ); | |
this._removeInactiveAction( action ); | |
} | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.AnimationMixer.prototype ); | |
THREE.AnimationMixer._Action = | |
function( mixer, clip, localRoot ) { | |
this._mixer = mixer; | |
this._clip = clip; | |
this._localRoot = localRoot || null; | |
var tracks = clip.tracks, | |
nTracks = tracks.length, | |
interpolants = new Array( nTracks ); | |
var interpolantSettings = { | |
endingStart: THREE.ZeroCurvatureEnding, | |
endingEnd: THREE.ZeroCurvatureEnding | |
}; | |
for ( var i = 0; i !== nTracks; ++ i ) { | |
var interpolant = tracks[ i ].createInterpolant( null ); | |
interpolants[ i ] = interpolant; | |
interpolant.settings = interpolantSettings | |
} | |
this._interpolantSettings = interpolantSettings; | |
this._interpolants = interpolants; // bound by the mixer | |
// inside: PropertyMixer (managed by the mixer) | |
this._propertyBindings = new Array( nTracks ); | |
this._cacheIndex = null; // for the memory manager | |
this._byClipCacheIndex = null; // for the memory manager | |
this._timeScaleInterpolant = null; | |
this._weightInterpolant = null; | |
this.loop = THREE.LoopRepeat; | |
this._loopCount = -1; | |
// global mixer time when the action is to be started | |
// it's set back to 'null' upon start of the action | |
this._startTime = null; | |
// scaled local time of the action | |
// gets clamped or wrapped to 0..clip.duration according to loop | |
this.time = 0; | |
this.timeScale = 1; | |
this._effectiveTimeScale = 1; | |
this.weight = 1; | |
this._effectiveWeight = 1; | |
this.repetitions = Infinity; // no. of repetitions when looping | |
this.paused = false; // false -> zero effective time scale | |
this.enabled = true; // true -> zero effective weight | |
this.clampWhenFinished = false; // keep feeding the last frame? | |
this.zeroSlopeAtStart = true; // for smooth interpolation w/o separate | |
this.zeroSlopeAtEnd = true; // clips for start, loop and end | |
}; | |
THREE.AnimationMixer._Action.prototype = { | |
constructor: THREE.AnimationMixer._Action, | |
// State & Scheduling | |
play: function() { | |
this._mixer._activateAction( this ); | |
return this; | |
}, | |
stop: function() { | |
this._mixer._deactivateAction( this ); | |
return this.reset(); | |
}, | |
reset: function() { | |
this.paused = false; | |
this.enabled = true; | |
this.time = 0; // restart clip | |
this._loopCount = -1; // forget previous loops | |
this._startTime = null; // forget scheduling | |
return this.stopFading().stopWarping(); | |
}, | |
isRunning: function() { | |
var start = this._startTime; | |
return this.enabled && ! this.paused && this.timeScale !== 0 && | |
this._startTime === null && this._mixer._isActiveAction( this ) | |
}, | |
// return true when play has been called | |
isScheduled: function() { | |
return this._mixer._isActiveAction( this ); | |
}, | |
startAt: function( time ) { | |
this._startTime = time; | |
return this; | |
}, | |
setLoop: function( mode, repetitions ) { | |
this.loop = mode; | |
this.repetitions = repetitions; | |
return this; | |
}, | |
// Weight | |
// set the weight stopping any scheduled fading | |
// although .enabled = false yields an effective weight of zero, this | |
// method does *not* change .enabled, because it would be confusing | |
setEffectiveWeight: function( weight ) { | |
this.weight = weight; | |
// note: same logic as when updated at runtime | |
this._effectiveWeight = this.enabled ? weight : 0; | |
return this.stopFading(); | |
}, | |
// return the weight considering fading and .enabled | |
getEffectiveWeight: function() { | |
return this._effectiveWeight; | |
}, | |
fadeIn: function( duration ) { | |
return this._scheduleFading( duration, 0, 1 ); | |
}, | |
fadeOut: function( duration ) { | |
return this._scheduleFading( duration, 1, 0 ); | |
}, | |
crossFadeFrom: function( fadeOutAction, duration, warp ) { | |
var mixer = this._mixer; | |
fadeOutAction.fadeOut( duration ); | |
this.fadeIn( duration ); | |
if( warp ) { | |
var fadeInDuration = this._clip.duration, | |
fadeOutDuration = fadeOutAction._clip.duration, | |
startEndRatio = fadeOutDuration / fadeInDuration, | |
endStartRatio = fadeInDuration / fadeOutDuration; | |
fadeOutAction.warp( 1.0, startEndRatio, duration ); | |
this.warp( endStartRatio, 1.0, duration ); | |
} | |
return this; | |
}, | |
crossFadeTo: function( fadeInAction, duration, warp ) { | |
return fadeInAction.crossFadeFrom( this, duration, warp ); | |
}, | |
stopFading: function() { | |
var weightInterpolant = this._weightInterpolant; | |
if ( weightInterpolant !== null ) { | |
this._weightInterpolant = null; | |
this._mixer._takeBackControlInterpolant( weightInterpolant ); | |
} | |
return this; | |
}, | |
// Time Scale Control | |
// set the weight stopping any scheduled warping | |
// although .paused = true yields an effective time scale of zero, this | |
// method does *not* change .paused, because it would be confusing | |
setEffectiveTimeScale: function( timeScale ) { | |
this.timeScale = timeScale; | |
this._effectiveTimeScale = this.paused ? 0 :timeScale; | |
return this.stopWarping(); | |
}, | |
// return the time scale considering warping and .paused | |
getEffectiveTimeScale: function() { | |
return this._effectiveTimeScale; | |
}, | |
setDuration: function( duration ) { | |
this.timeScale = this._clip.duration / duration; | |
return this.stopWarping(); | |
}, | |
syncWith: function( action ) { | |
this.time = action.time; | |
this.timeScale = action.timeScale; | |
return this.stopWarping(); | |
}, | |
halt: function( duration ) { | |
return this.warp( this._currentTimeScale, 0, duration ); | |
}, | |
warp: function( startTimeScale, endTimeScale, duration ) { | |
var mixer = this._mixer, now = mixer.time, | |
interpolant = this._timeScaleInterpolant, | |
timeScale = this.timeScale; | |
if ( interpolant === null ) { | |
interpolant = mixer._lendControlInterpolant(), | |
this._timeScaleInterpolant = interpolant; | |
} | |
var times = interpolant.parameterPositions, | |
values = interpolant.sampleValues; | |
times[ 0 ] = now; | |
times[ 1 ] = now + duration; | |
values[ 0 ] = startTimeScale / timeScale; | |
values[ 1 ] = endTimeScale / timeScale; | |
return this; | |
}, | |
stopWarping: function() { | |
var timeScaleInterpolant = this._timeScaleInterpolant; | |
if ( timeScaleInterpolant !== null ) { | |
this._timeScaleInterpolant = null; | |
this._mixer._takeBackControlInterpolant( timeScaleInterpolant ); | |
} | |
return this; | |
}, | |
// Object Accessors | |
getMixer: function() { | |
return this._mixer; | |
}, | |
getClip: function() { | |
return this._clip; | |
}, | |
getRoot: function() { | |
return this._localRoot || this._mixer._root; | |
}, | |
// Interna | |
_update: function( time, deltaTime, timeDirection, accuIndex ) { | |
// called by the mixer | |
var startTime = this._startTime; | |
if ( startTime !== null ) { | |
// check for scheduled start of action | |
var timeRunning = ( time - startTime ) * timeDirection; | |
if ( timeRunning < 0 || timeDirection === 0 ) { | |
return; // yet to come / don't decide when delta = 0 | |
} | |
// start | |
this._startTime = null; // unschedule | |
deltaTime = timeDirection * timeRunning; | |
} | |
// apply time scale and advance time | |
deltaTime *= this._updateTimeScale( time ); | |
var clipTime = this._updateTime( deltaTime ); | |
// note: _updateTime may disable the action resulting in | |
// an effective weight of 0 | |
var weight = this._updateWeight( time ); | |
if ( weight > 0 ) { | |
var interpolants = this._interpolants; | |
var propertyMixers = this._propertyBindings; | |
for ( var j = 0, m = interpolants.length; j !== m; ++ j ) { | |
interpolants[ j ].evaluate( clipTime ); | |
propertyMixers[ j ].accumulate( accuIndex, weight ); | |
} | |
} | |
}, | |
_updateWeight: function( time ) { | |
var weight = 0; | |
if ( this.enabled ) { | |
weight = this.weight; | |
var interpolant = this._weightInterpolant; | |
if ( interpolant !== null ) { | |
var interpolantValue = interpolant.evaluate( time )[ 0 ]; | |
weight *= interpolantValue; | |
if ( time > interpolant.parameterPositions[ 1 ] ) { | |
this.stopFading(); | |
if ( interpolantValue === 0 ) { | |
// faded out, disable | |
this.enabled = false; | |
} | |
} | |
} | |
} | |
this._effectiveWeight = weight; | |
return weight; | |
}, | |
_updateTimeScale: function( time ) { | |
var timeScale = 0; | |
if ( ! this.paused ) { | |
timeScale = this.timeScale; | |
var interpolant = this._timeScaleInterpolant; | |
if ( interpolant !== null ) { | |
var interpolantValue = interpolant.evaluate( time )[ 0 ]; | |
timeScale *= interpolantValue; | |
if ( time > interpolant.parameterPositions[ 1 ] ) { | |
this.stopWarping(); | |
if ( timeScale === 0 ) { | |
// motion has halted, pause | |
this.pause = true; | |
} else { | |
// warp done - apply final time scale | |
this.timeScale = timeScale; | |
} | |
} | |
} | |
} | |
this._effectiveTimeScale = timeScale; | |
return timeScale; | |
}, | |
_updateTime: function( deltaTime ) { | |
var time = this.time + deltaTime; | |
if ( deltaTime === 0 ) return time; | |
var duration = this._clip.duration, | |
loop = this.loop, | |
loopCount = this._loopCount, | |
pingPong = false; | |
switch ( loop ) { | |
case THREE.LoopOnce: | |
if ( loopCount === -1 ) { | |
// just started | |
this.loopCount = 0; | |
this._setEndings( true, true, false ); | |
} | |
if ( time >= duration ) { | |
time = duration; | |
} else if ( time < 0 ) { | |
time = 0; | |
} else break; | |
// reached the end | |
if ( this.clampWhenFinished ) this.pause = true; | |
else this.enabled = false; | |
this._mixer.dispatchEvent( { | |
type: 'finished', action: this, | |
direction: deltaTime < 0 ? -1 : 1 | |
} ); | |
break; | |
case THREE.LoopPingPong: | |
pingPong = true; | |
case THREE.LoopRepeat: | |
if ( loopCount === -1 ) { | |
// just started | |
if ( deltaTime > 0 ) { | |
loopCount = 0; | |
this._setEndings( | |
true, this.repetitions === 0, pingPong ); | |
} else { | |
// when looping in reverse direction, the initial | |
// transition through zero counts as a repetition, | |
// so leave loopCount at -1 | |
this._setEndings( | |
this.repetitions === 0, true, pingPong ); | |
} | |
} | |
if ( time >= duration || time < 0 ) { | |
// wrap around | |
var loopDelta = Math.floor( time / duration ); // signed | |
time -= duration * loopDelta; | |
loopCount += Math.abs( loopDelta ); | |
var pending = this.repetitions - loopCount; | |
if ( pending < 0 ) { | |
// stop (switch state, clamp time, fire event) | |
if ( this.clampWhenFinished ) this.paused = true; | |
else this.enabled = false; | |
time = deltaTime > 0 ? duration : 0; | |
this._mixer.dispatchEvent( { | |
type: 'finished', action: this, | |
direction: deltaTime > 0 ? 1 : -1 | |
} ); | |
break; | |
} else if ( pending === 0 ) { | |
// transition to last round | |
var atStart = deltaTime < 0; | |
this._setEndings( atStart, ! atStart, pingPong ); | |
} else { | |
this._setEndings( false, false, pingPong ); | |
} | |
this._loopCount = loopCount; | |
this._mixer.dispatchEvent( { | |
type: 'loop', action: this, loopDelta: loopDelta | |
} ); | |
} | |
if ( loop === THREE.LoopPingPong && ( loopCount & 1 ) === 1 ) { | |
// invert time for the "pong round" | |
this.time = time; | |
return duration - time; | |
} | |
break; | |
} | |
this.time = time; | |
return time; | |
}, | |
_setEndings: function( atStart, atEnd, pingPong ) { | |
var settings = this._interpolantSettings; | |
if ( pingPong ) { | |
settings.endingStart = THREE.ZeroSlopeEnding; | |
settings.endingEnd = THREE.ZeroSlopeEnding; | |
} else { | |
// assuming for LoopOnce atStart == atEnd == true | |
if ( atStart ) { | |
settings.endingStart = this.zeroSlopeAtStart ? | |
THREE.ZeroSlopeEnding : THREE.ZeroCurvatureEnding; | |
} else { | |
settings.endingStart = THREE.WrapAroundEnding; | |
} | |
if ( atEnd ) { | |
settings.endingEnd = this.zeroSlopeAtEnd ? | |
THREE.ZeroSlopeEnding : THREE.ZeroCurvatureEnding; | |
} else { | |
settings.endingEnd = THREE.WrapAroundEnding; | |
} | |
} | |
}, | |
_scheduleFading: function( duration, weightNow, weightThen ) { | |
var mixer = this._mixer, now = mixer.time, | |
interpolant = this._weightInterpolant; | |
if ( interpolant === null ) { | |
interpolant = mixer._lendControlInterpolant(), | |
this._weightInterpolant = interpolant; | |
} | |
var times = interpolant.parameterPositions, | |
values = interpolant.sampleValues; | |
times[ 0 ] = now; values[ 0 ] = weightNow; | |
times[ 1 ] = now + duration; values[ 1 ] = weightThen; | |
return this; | |
} | |
}; | |
// Implementation details: | |
Object.assign( THREE.AnimationMixer.prototype, { | |
_bindAction: function( action, prototypeAction ) { | |
var root = action._localRoot || this._root, | |
tracks = action._clip.tracks, | |
nTracks = tracks.length, | |
bindings = action._propertyBindings, | |
interpolants = action._interpolants, | |
rootUuid = root.uuid, | |
bindingsByRoot = this._bindingsByRootAndName, | |
bindingsByName = bindingsByRoot[ rootUuid ]; | |
if ( bindingsByName === undefined ) { | |
bindingsByName = {}; | |
bindingsByRoot[ rootUuid ] = bindingsByName; | |
} | |
for ( var i = 0; i !== nTracks; ++ i ) { | |
var track = tracks[ i ], | |
trackName = track.name, | |
binding = bindingsByName[ trackName ]; | |
if ( binding !== undefined ) { | |
bindings[ i ] = binding; | |
} else { | |
binding = bindings[ i ]; | |
if ( binding !== undefined ) { | |
// existing binding, make sure the cache knows | |
if ( binding._cacheIndex === null ) { | |
++ binding.referenceCount; | |
this._addInactiveBinding( binding, rootUuid, trackName ); | |
} | |
continue; | |
} | |
var path = prototypeAction && prototypeAction. | |
_propertyBindings[ i ].binding.parsedPath; | |
binding = new THREE.PropertyMixer( | |
THREE.PropertyBinding.create( root, trackName, path ), | |
track.ValueTypeName, track.getValueSize() ); | |
++ binding.referenceCount; | |
this._addInactiveBinding( binding, rootUuid, trackName ); | |
bindings[ i ] = binding; | |
} | |
interpolants[ i ].resultBuffer = binding.buffer; | |
} | |
}, | |
_activateAction: function( action ) { | |
if ( ! this._isActiveAction( action ) ) { | |
if ( action._cacheIndex === null ) { | |
// this action has been forgotten by the cache, but the user | |
// appears to be still using it -> rebind | |
var rootUuid = ( action._localRoot || this._root ).uuid, | |
clipName = action._clip.name, | |
actionsForClip = this._actionsByClip[ clipName ]; | |
this._bindAction( action, | |
actionsForClip && actionsForClip.knownActions[ 0 ] ); | |
this._addInactiveAction( action, clipName, rootUuid ); | |
} | |
var bindings = action._propertyBindings; | |
// increment reference counts / sort out state | |
for ( var i = 0, n = bindings.length; i !== n; ++ i ) { | |
var binding = bindings[ i ]; | |
if ( binding.useCount ++ === 0 ) { | |
this._lendBinding( binding ); | |
binding.saveOriginalState(); | |
} | |
} | |
this._lendAction( action ); | |
} | |
}, | |
_deactivateAction: function( action ) { | |
if ( this._isActiveAction( action ) ) { | |
var bindings = action._propertyBindings; | |
// decrement reference counts / sort out state | |
for ( var i = 0, n = bindings.length; i !== n; ++ i ) { | |
var binding = bindings[ i ]; | |
if ( -- binding.useCount === 0 ) { | |
binding.restoreOriginalState(); | |
this._takeBackBinding( binding ); | |
} | |
} | |
this._takeBackAction( action ); | |
} | |
}, | |
// Memory manager | |
_initMemoryManager: function() { | |
this._actions = []; // 'nActiveActions' followed by inactive ones | |
this._nActiveActions = 0; | |
this._actionsByClip = {}; | |
// inside: | |
// { | |
// knownActions: Array< _Action > - used as prototypes | |
// actionByRoot: _Action - lookup | |
// } | |
this._bindings = []; // 'nActiveBindings' followed by inactive ones | |
this._nActiveBindings = 0; | |
this._bindingsByRootAndName = {}; // inside: Map< name, PropertyMixer > | |
this._controlInterpolants = []; // same game as above | |
this._nActiveControlInterpolants = 0; | |
var scope = this; | |
this.stats = { | |
actions: { | |
get total() { return scope._actions.length; }, | |
get inUse() { return scope._nActiveActions; } | |
}, | |
bindings: { | |
get total() { return scope._bindings.length; }, | |
get inUse() { return scope._nActiveBindings; } | |
}, | |
controlInterpolants: { | |
get total() { return scope._controlInterpolants.length; }, | |
get inUse() { return scope._nActiveControlInterpolants; } | |
} | |
}; | |
}, | |
// Memory management for _Action objects | |
_isActiveAction: function( action ) { | |
var index = action._cacheIndex; | |
return index !== null && index < this._nActiveActions; | |
}, | |
_addInactiveAction: function( action, clipName, rootUuid ) { | |
var actions = this._actions, | |
actionsByClip = this._actionsByClip, | |
actionsForClip = actionsByClip[ clipName ]; | |
if ( actionsForClip === undefined ) { | |
actionsForClip = { | |
knownActions: [ action ], | |
actionByRoot: {} | |
}; | |
action._byClipCacheIndex = 0; | |
actionsByClip[ clipName ] = actionsForClip; | |
} else { | |
var knownActions = actionsForClip.knownActions; | |
action._byClipCacheIndex = knownActions.length; | |
knownActions.push( action ); | |
} | |
action._cacheIndex = actions.length; | |
actions.push( action ); | |
actionsForClip.actionByRoot[ rootUuid ] = action; | |
}, | |
_removeInactiveAction: function( action ) { | |
var actions = this._actions, | |
lastInactiveAction = actions[ actions.length - 1 ], | |
cacheIndex = action._cacheIndex; | |
lastInactiveAction._cacheIndex = cacheIndex; | |
actions[ cacheIndex ] = lastInactiveAction; | |
actions.pop(); | |
action._cacheIndex = null; | |
var clipName = action._clip.name, | |
actionsByClip = this._actionsByClip, | |
actionsForClip = actionsByClip[ clipName ], | |
knownActionsForClip = actionsForClip.knownActions, | |
lastKnownAction = | |
knownActionsForClip[ knownActionsForClip.length - 1 ], | |
byClipCacheIndex = action._byClipCacheIndex; | |
lastKnownAction._byClipCacheIndex = byClipCacheIndex; | |
knownActionsForClip[ byClipCacheIndex ] = lastKnownAction; | |
knownActionsForClip.pop(); | |
action._byClipCacheIndex = null; | |
var actionByRoot = actionsForClip.actionByRoot, | |
rootUuid = ( actions._localRoot || this._root ).uuid; | |
delete actionByRoot[ rootUuid ]; | |
if ( knownActionsForClip.length === 0 ) { | |
delete actionsByClip[ clipName ]; | |
} | |
this._removeInactiveBindingsForAction( action ); | |
}, | |
_removeInactiveBindingsForAction: function( action ) { | |
var bindings = action._propertyBindings; | |
for ( var i = 0, n = bindings.length; i !== n; ++ i ) { | |
var binding = bindings[ i ]; | |
if ( -- binding.referenceCount === 0 ) { | |
this._removeInactiveBinding( binding ); | |
} | |
} | |
}, | |
_lendAction: function( action ) { | |
// [ active actions | inactive actions ] | |
// [ active actions >| inactive actions ] | |
// s a | |
// <-swap-> | |
// a s | |
var actions = this._actions, | |
prevIndex = action._cacheIndex, | |
lastActiveIndex = this._nActiveActions ++, | |
firstInactiveAction = actions[ lastActiveIndex ]; | |
action._cacheIndex = lastActiveIndex; | |
actions[ lastActiveIndex ] = action; | |
firstInactiveAction._cacheIndex = prevIndex; | |
actions[ prevIndex ] = firstInactiveAction; | |
}, | |
_takeBackAction: function( action ) { | |
// [ active actions | inactive actions ] | |
// [ active actions |< inactive actions ] | |
// a s | |
// <-swap-> | |
// s a | |
var actions = this._actions, | |
prevIndex = action._cacheIndex, | |
firstInactiveIndex = -- this._nActiveActions, | |
lastActiveAction = actions[ firstInactiveIndex ]; | |
action._cacheIndex = firstInactiveIndex; | |
actions[ firstInactiveIndex ] = action; | |
lastActiveAction._cacheIndex = prevIndex; | |
actions[ prevIndex ] = lastActiveAction; | |
}, | |
// Memory management for PropertyMixer objects | |
_addInactiveBinding: function( binding, rootUuid, trackName ) { | |
var bindingsByRoot = this._bindingsByRootAndName, | |
bindingByName = bindingsByRoot[ rootUuid ], | |
bindings = this._bindings; | |
if ( bindingByName === undefined ) { | |
bindingByName = {}; | |
bindingsByRoot[ rootUuid ] = bindingByName; | |
} | |
bindingByName[ trackName ] = binding; | |
binding._cacheIndex = bindings.length; | |
bindings.push( binding ); | |
}, | |
_removeInactiveBinding: function( binding ) { | |
var bindings = this._bindings, | |
propBinding = binding.binding, | |
rootUuid = propBinding.rootNode.uuid, | |
trackName = propBinding.path, | |
bindingsByRoot = this._bindingsByRootAndName, | |
bindingByName = bindingsByRoot[ rootUuid ], | |
lastInactiveBinding = bindings[ bindings.length - 1 ], | |
cacheIndex = binding._cacheIndex; | |
lastInactiveBinding._cacheIndex = cacheIndex; | |
bindings[ cacheIndex ] = lastInactiveBinding; | |
bindings.pop(); | |
delete bindingByName[ trackName ]; | |
remove_empty_map: { | |
for ( var _ in bindingByName ) break remove_empty_map; | |
delete bindingsByRoot[ rootUuid ]; | |
} | |
}, | |
_lendBinding: function( binding ) { | |
var bindings = this._bindings, | |
prevIndex = binding._cacheIndex, | |
lastActiveIndex = this._nActiveBindings ++, | |
firstInactiveBinding = bindings[ lastActiveIndex ]; | |
binding._cacheIndex = lastActiveIndex; | |
bindings[ lastActiveIndex ] = binding; | |
firstInactiveBinding._cacheIndex = prevIndex; | |
bindings[ prevIndex ] = firstInactiveBinding; | |
}, | |
_takeBackBinding: function( binding ) { | |
var bindings = this._bindings, | |
prevIndex = binding._cacheIndex, | |
firstInactiveIndex = -- this._nActiveBindings, | |
lastActiveBinding = bindings[ firstInactiveIndex ]; | |
binding._cacheIndex = firstInactiveIndex; | |
bindings[ firstInactiveIndex ] = binding; | |
lastActiveBinding._cacheIndex = prevIndex; | |
bindings[ prevIndex ] = lastActiveBinding; | |
}, | |
// Memory management of Interpolants for weight and time scale | |
_lendControlInterpolant: function() { | |
var interpolants = this._controlInterpolants, | |
lastActiveIndex = this._nActiveControlInterpolants ++, | |
interpolant = interpolants[ lastActiveIndex ]; | |
if ( interpolant === undefined ) { | |
interpolant = new THREE.LinearInterpolant( | |
new Float32Array( 2 ), new Float32Array( 2 ), | |
1, this._controlInterpolantsResultBuffer ); | |
interpolant.__cacheIndex = lastActiveIndex; | |
interpolants[ lastActiveIndex ] = interpolant; | |
} | |
return interpolant; | |
}, | |
_takeBackControlInterpolant: function( interpolant ) { | |
var interpolants = this._controlInterpolants, | |
prevIndex = interpolant.__cacheIndex, | |
firstInactiveIndex = -- this._nActiveControlInterpolants, | |
lastActiveInterpolant = interpolants[ firstInactiveIndex ]; | |
interpolant.__cacheIndex = firstInactiveIndex; | |
interpolants[ firstInactiveIndex ] = interpolant; | |
lastActiveInterpolant.__cacheIndex = prevIndex; | |
interpolants[ prevIndex ] = lastActiveInterpolant; | |
}, | |
_controlInterpolantsResultBuffer: new Float32Array( 1 ) | |
} ); | |
// File:src/animation/AnimationObjectGroup.js | |
/** | |
* | |
* A group of objects that receives a shared animation state. | |
* | |
* Usage: | |
* | |
* - Add objects you would otherwise pass as 'root' to the | |
* constructor or the .clipAction method of AnimationMixer. | |
* | |
* - Instead pass this object as 'root'. | |
* | |
* - You can also add and remove objects later when the mixer | |
* is running. | |
* | |
* Note: | |
* | |
* Objects of this class appear as one object to the mixer, | |
* so cache control of the individual objects must be done | |
* on the group. | |
* | |
* Limitation: | |
* | |
* - The animated properties must be compatible among the | |
* all objects in the group. | |
* | |
* - A single property can either be controlled through a | |
* target group or directly, but not both. | |
* | |
* @author tschw | |
*/ | |
THREE.AnimationObjectGroup = function( var_args ) { | |
this.uuid = THREE.Math.generateUUID(); | |
// cached objects followed by the active ones | |
this._objects = Array.prototype.slice.call( arguments ); | |
this.nCachedObjects_ = 0; // threshold | |
// note: read by PropertyBinding.Composite | |
var indices = {}; | |
this._indicesByUUID = indices; // for bookkeeping | |
for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
indices[ arguments[ i ].uuid ] = i; | |
} | |
this._paths = []; // inside: string | |
this._parsedPaths = []; // inside: { we don't care, here } | |
this._bindings = []; // inside: Array< PropertyBinding > | |
this._bindingsIndicesByPath = {}; // inside: indices in these arrays | |
var scope = this; | |
this.stats = { | |
objects: { | |
get total() { return scope._objects.length; }, | |
get inUse() { return this.total - scope.nCachedObjects_; } | |
}, | |
get bindingsPerObject() { return scope._bindings.length; } | |
}; | |
}; | |
THREE.AnimationObjectGroup.prototype = { | |
constructor: THREE.AnimationObjectGroup, | |
add: function( var_args ) { | |
var objects = this._objects, | |
nObjects = objects.length, | |
nCachedObjects = this.nCachedObjects_, | |
indicesByUUID = this._indicesByUUID, | |
paths = this._paths, | |
parsedPaths = this._parsedPaths, | |
bindings = this._bindings, | |
nBindings = bindings.length; | |
for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
var object = arguments[ i ], | |
uuid = object.uuid, | |
index = indicesByUUID[ uuid ]; | |
if ( index === undefined ) { | |
// unknown object -> add it to the ACTIVE region | |
index = nObjects ++; | |
indicesByUUID[ uuid ] = index; | |
objects.push( object ); | |
// accounting is done, now do the same for all bindings | |
for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
bindings[ j ].push( | |
new THREE.PropertyBinding( | |
object, paths[ j ], parsedPaths[ j ] ) ); | |
} | |
} else if ( index < nCachedObjects ) { | |
var knownObject = objects[ index ]; | |
// move existing object to the ACTIVE region | |
var firstActiveIndex = -- nCachedObjects, | |
lastCachedObject = objects[ firstActiveIndex ]; | |
indicesByUUID[ lastCachedObject.uuid ] = index; | |
objects[ index ] = lastCachedObject; | |
indicesByUUID[ uuid ] = firstActiveIndex; | |
objects[ firstActiveIndex ] = object; | |
// accounting is done, now do the same for all bindings | |
for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
var bindingsForPath = bindings[ j ], | |
lastCached = bindingsForPath[ firstActiveIndex ], | |
binding = bindingsForPath[ index ]; | |
bindingsForPath[ index ] = lastCached; | |
if ( binding === undefined ) { | |
// since we do not bother to create new bindings | |
// for objects that are cached, the binding may | |
// or may not exist | |
binding = new THREE.PropertyBinding( | |
object, paths[ j ], parsedPaths[ j ] ); | |
} | |
bindingsForPath[ firstActiveIndex ] = binding; | |
} | |
} else if ( objects[ index ] !== knownObject) { | |
console.error( "Different objects with the same UUID " + | |
"detected. Clean the caches or recreate your " + | |
"infrastructure when reloading scenes..." ); | |
} // else the object is already where we want it to be | |
} // for arguments | |
this.nCachedObjects_ = nCachedObjects; | |
}, | |
remove: function( var_args ) { | |
var objects = this._objects, | |
nObjects = objects.length, | |
nCachedObjects = this.nCachedObjects_, | |
indicesByUUID = this._indicesByUUID, | |
bindings = this._bindings, | |
nBindings = bindings.length; | |
for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
var object = arguments[ i ], | |
uuid = object.uuid, | |
index = indicesByUUID[ uuid ]; | |
if ( index !== undefined && index >= nCachedObjects ) { | |
// move existing object into the CACHED region | |
var lastCachedIndex = nCachedObjects ++, | |
firstActiveObject = objects[ lastCachedIndex ]; | |
indicesByUUID[ firstActiveObject.uuid ] = index; | |
objects[ index ] = firstActiveObject; | |
indicesByUUID[ uuid ] = lastCachedIndex; | |
objects[ lastCachedIndex ] = object; | |
// accounting is done, now do the same for all bindings | |
for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
var bindingsForPath = bindings[ j ], | |
firstActive = bindingsForPath[ lastCachedIndex ], | |
binding = bindingsForPath[ index ]; | |
bindingsForPath[ index ] = firstActive; | |
bindingsForPath[ lastCachedIndex ] = binding; | |
} | |
} | |
} // for arguments | |
this.nCachedObjects_ = nCachedObjects; | |
}, | |
// remove & forget | |
uncache: function( var_args ) { | |
var objects = this._objects, | |
nObjects = objects.length, | |
nCachedObjects = this.nCachedObjects_, | |
indicesByUUID = this._indicesByUUID, | |
bindings = this._bindings, | |
nBindings = bindings.length; | |
for ( var i = 0, n = arguments.length; i !== n; ++ i ) { | |
var object = arguments[ i ], | |
uuid = object.uuid, | |
index = indicesByUUID[ uuid ]; | |
if ( index !== undefined ) { | |
delete indicesByUUID[ uuid ]; | |
if ( index < nCachedObjects ) { | |
// object is cached, shrink the CACHED region | |
var firstActiveIndex = -- nCachedObjects, | |
lastCachedObject = objects[ firstActiveIndex ], | |
lastIndex = -- nObjects, | |
lastObject = objects[ lastIndex ]; | |
// last cached object takes this object's place | |
indicesByUUID[ lastCachedObject.uuid ] = index; | |
objects[ index ] = lastCachedObject; | |
// last object goes to the activated slot and pop | |
indicesByUUID[ lastObject.uuid ] = firstActiveIndex; | |
objects[ firstActiveIndex ] = lastObject; | |
objects.pop(); | |
// accounting is done, now do the same for all bindings | |
for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
var bindingsForPath = bindings[ j ], | |
lastCached = bindingsForPath[ firstActiveIndex ], | |
last = bindingsForPath[ lastIndex ]; | |
bindingsForPath[ index ] = lastCached; | |
bindingsForPath[ firstActiveIndex ] = last; | |
bindingsForPath.pop(); | |
} | |
} else { | |
// object is active, just swap with the last and pop | |
var lastIndex = -- nObjects, | |
lastObject = objects[ lastIndex ]; | |
indicesByUUID[ lastObject.uuid ] = index; | |
objects[ index ] = lastObject; | |
objects.pop(); | |
// accounting is done, now do the same for all bindings | |
for ( var j = 0, m = nBindings; j !== m; ++ j ) { | |
var bindingsForPath = bindings[ j ]; | |
bindingsForPath[ index ] = bindingsForPath[ lastIndex ]; | |
bindingsForPath.pop(); | |
} | |
} // cached or active | |
} // if object is known | |
} // for arguments | |
this.nCachedObjects_ = nCachedObjects; | |
}, | |
// Internal interface used by befriended PropertyBinding.Composite: | |
subscribe_: function( path, parsedPath ) { | |
// returns an array of bindings for the given path that is changed | |
// according to the contained objects in the group | |
var indicesByPath = this._bindingsIndicesByPath, | |
index = indicesByPath[ path ], | |
bindings = this._bindings; | |
if ( index !== undefined ) return bindings[ index ]; | |
var paths = this._paths, | |
parsedPaths = this._parsedPaths, | |
objects = this._objects, | |
nObjects = objects.length, | |
nCachedObjects = this.nCachedObjects_, | |
bindingsForPath = new Array( nObjects ); | |
index = bindings.length; | |
indicesByPath[ path ] = index; | |
paths.push( path ); | |
parsedPaths.push( parsedPath ); | |
bindings.push( bindingsForPath ); | |
for ( var i = nCachedObjects, | |
n = objects.length; i !== n; ++ i ) { | |
var object = objects[ i ]; | |
bindingsForPath[ i ] = | |
new THREE.PropertyBinding( object, path, parsedPath ); | |
} | |
return bindingsForPath; | |
}, | |
unsubscribe_: function( path ) { | |
// tells the group to forget about a property path and no longer | |
// update the array previously obtained with 'subscribe_' | |
var indicesByPath = this._bindingsIndicesByPath, | |
index = indicesByPath[ path ]; | |
if ( index !== undefined ) { | |
var paths = this._paths, | |
parsedPaths = this._parsedPaths, | |
bindings = this._bindings, | |
lastBindingsIndex = bindings.length - 1, | |
lastBindings = bindings[ lastBindingsIndex ], | |
lastBindingsPath = path[ lastBindingsIndex ]; | |
indicesByPath[ lastBindingsPath ] = index; | |
bindings[ index ] = lastBindings; | |
bindings.pop(); | |
parsedPaths[ index ] = parsedPaths[ lastBindingsIndex ]; | |
parsedPaths.pop(); | |
paths[ index ] = paths[ lastBindingsIndex ]; | |
paths.pop(); | |
} | |
} | |
}; | |
// File:src/animation/AnimationUtils.js | |
/** | |
* @author tschw | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
*/ | |
THREE.AnimationUtils = { | |
// same as Array.prototype.slice, but also works on typed arrays | |
arraySlice: function( array, from, to ) { | |
if ( THREE.AnimationUtils.isTypedArray( array ) ) { | |
return new array.constructor( array.subarray( from, to ) ); | |
} | |
return array.slice( from, to ); | |
}, | |
// converts an array to a specific type | |
convertArray: function( array, type, forceClone ) { | |
if ( ! array || // let 'undefined' and 'null' pass | |
! forceClone && array.constructor === type ) return array; | |
if ( typeof type.BYTES_PER_ELEMENT === 'number' ) { | |
return new type( array ); // create typed array | |
} | |
return Array.prototype.slice.call( array ); // create Array | |
}, | |
isTypedArray: function( object ) { | |
return ArrayBuffer.isView( object ) && | |
! ( object instanceof DataView ); | |
}, | |
// returns an array by which times and values can be sorted | |
getKeyframeOrder: function( times ) { | |
function compareTime( i, j ) { | |
return times[ i ] - times[ j ]; | |
} | |
var n = times.length; | |
var result = new Array( n ); | |
for ( var i = 0; i !== n; ++ i ) result[ i ] = i; | |
result.sort( compareTime ); | |
return result; | |
}, | |
// uses the array previously returned by 'getKeyframeOrder' to sort data | |
sortedArray: function( values, stride, order ) { | |
var nValues = values.length; | |
var result = new values.constructor( nValues ); | |
for ( var i = 0, dstOffset = 0; dstOffset !== nValues; ++ i ) { | |
var srcOffset = order[ i ] * stride; | |
for ( var j = 0; j !== stride; ++ j ) { | |
result[ dstOffset ++ ] = values[ srcOffset + j ]; | |
} | |
} | |
return result; | |
}, | |
// function for parsing AOS keyframe formats | |
flattenJSON: function( jsonKeys, times, values, valuePropertyName ) { | |
var i = 1, key = jsonKeys[ 0 ]; | |
while ( key !== undefined && key[ valuePropertyName ] === undefined ) { | |
key = jsonKeys[ i ++ ]; | |
} | |
if ( key === undefined ) return; // no data | |
var value = key[ valuePropertyName ]; | |
if ( value === undefined ) return; // no data | |
if ( Array.isArray( value ) ) { | |
do { | |
value = key[ valuePropertyName ]; | |
if ( value !== undefined ) { | |
times.push( key.time ); | |
values.push.apply( values, value ); // push all elements | |
} | |
key = jsonKeys[ i ++ ]; | |
} while ( key !== undefined ); | |
} else if ( value.toArray !== undefined ) { | |
// ...assume THREE.Math-ish | |
do { | |
value = key[ valuePropertyName ]; | |
if ( value !== undefined ) { | |
times.push( key.time ); | |
value.toArray( values, values.length ); | |
} | |
key = jsonKeys[ i ++ ]; | |
} while ( key !== undefined ); | |
} else { | |
// otherwise push as-is | |
do { | |
value = key[ valuePropertyName ]; | |
if ( value !== undefined ) { | |
times.push( key.time ); | |
values.push( value ); | |
} | |
key = jsonKeys[ i ++ ]; | |
} while ( key !== undefined ); | |
} | |
} | |
}; | |
// File:src/animation/KeyframeTrack.js | |
/** | |
* | |
* A timed sequence of keyframes for a specific property. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.KeyframeTrack = function ( name, times, values, interpolation ) { | |
if( name === undefined ) throw new Error( "track name is undefined" ); | |
if( times === undefined || times.length === 0 ) { | |
throw new Error( "no keyframes in track named " + name ); | |
} | |
this.name = name; | |
this.times = THREE.AnimationUtils.convertArray( times, this.TimeBufferType ); | |
this.values = THREE.AnimationUtils.convertArray( values, this.ValueBufferType ); | |
this.setInterpolation( interpolation || this.DefaultInterpolation ); | |
this.validate(); | |
this.optimize(); | |
}; | |
THREE.KeyframeTrack.prototype = { | |
constructor: THREE.KeyframeTrack, | |
TimeBufferType: Float32Array, | |
ValueBufferType: Float32Array, | |
DefaultInterpolation: THREE.InterpolateLinear, | |
InterpolantFactoryMethodDiscrete: function( result ) { | |
return new THREE.DiscreteInterpolant( | |
this.times, this.values, this.getValueSize(), result ); | |
}, | |
InterpolantFactoryMethodLinear: function( result ) { | |
return new THREE.LinearInterpolant( | |
this.times, this.values, this.getValueSize(), result ); | |
}, | |
InterpolantFactoryMethodSmooth: function( result ) { | |
return new THREE.CubicInterpolant( | |
this.times, this.values, this.getValueSize(), result ); | |
}, | |
setInterpolation: function( interpolation ) { | |
var factoryMethod = undefined; | |
switch ( interpolation ) { | |
case THREE.InterpolateDiscrete: | |
factoryMethod = this.InterpolantFactoryMethodDiscrete; | |
break; | |
case THREE.InterpolateLinear: | |
factoryMethod = this.InterpolantFactoryMethodLinear; | |
break; | |
case THREE.InterpolateSmooth: | |
factoryMethod = this.InterpolantFactoryMethodSmooth; | |
break; | |
} | |
if ( factoryMethod === undefined ) { | |
var message = "unsupported interpolation for " + | |
this.ValueTypeName + " keyframe track named " + this.name; | |
if ( this.createInterpolant === undefined ) { | |
// fall back to default, unless the default itself is messed up | |
if ( interpolation !== this.DefaultInterpolation ) { | |
this.setInterpolation( this.DefaultInterpolation ); | |
} else { | |
throw new Error( message ); // fatal, in this case | |
} | |
} | |
console.warn( message ); | |
return; | |
} | |
this.createInterpolant = factoryMethod; | |
}, | |
getInterpolation: function() { | |
switch ( this.createInterpolant ) { | |
case this.InterpolantFactoryMethodDiscrete: | |
return THREE.InterpolateDiscrete; | |
case this.InterpolantFactoryMethodLinear: | |
return THREE.InterpolateLinear; | |
case this.InterpolantFactoryMethodSmooth: | |
return THREE.InterpolateSmooth; | |
} | |
}, | |
getValueSize: function() { | |
return this.values.length / this.times.length; | |
}, | |
// move all keyframes either forwards or backwards in time | |
shift: function( timeOffset ) { | |
if( timeOffset !== 0.0 ) { | |
var times = this.times; | |
for( var i = 0, n = times.length; i !== n; ++ i ) { | |
times[ i ] += timeOffset; | |
} | |
} | |
return this; | |
}, | |
// scale all keyframe times by a factor (useful for frame <-> seconds conversions) | |
scale: function( timeScale ) { | |
if( timeScale !== 1.0 ) { | |
var times = this.times; | |
for( var i = 0, n = times.length; i !== n; ++ i ) { | |
times[ i ] *= timeScale; | |
} | |
} | |
return this; | |
}, | |
// removes keyframes before and after animation without changing any values within the range [startTime, endTime]. | |
// IMPORTANT: We do not shift around keys to the start of the track time, because for interpolated keys this will change their values | |
trim: function( startTime, endTime ) { | |
var times = this.times, | |
nKeys = times.length, | |
from = 0, | |
to = nKeys - 1; | |
while ( from !== nKeys && times[ from ] < startTime ) ++ from; | |
while ( to !== -1 && times[ to ] > endTime ) -- to; | |
++ to; // inclusive -> exclusive bound | |
if( from !== 0 || to !== nKeys ) { | |
// empty tracks are forbidden, so keep at least one keyframe | |
if ( from >= to ) to = Math.max( to , 1 ), from = to - 1; | |
var stride = this.getValueSize(); | |
this.times = THREE.AnimationUtils.arraySlice( times, from, to ); | |
this.values = THREE.AnimationUtils. | |
arraySlice( this.values, from * stride, to * stride ); | |
} | |
return this; | |
}, | |
// ensure we do not get a GarbageInGarbageOut situation, make sure tracks are at least minimally viable | |
validate: function() { | |
var valid = true; | |
var valueSize = this.getValueSize(); | |
if ( valueSize - Math.floor( valueSize ) !== 0 ) { | |
console.error( "invalid value size in track", this ); | |
valid = false; | |
} | |
var times = this.times, | |
values = this.values, | |
nKeys = times.length; | |
if( nKeys === 0 ) { | |
console.error( "track is empty", this ); | |
valid = false; | |
} | |
var prevTime = null; | |
for( var i = 0; i !== nKeys; i ++ ) { | |
var currTime = times[ i ]; | |
if ( typeof currTime === 'number' && isNaN( currTime ) ) { | |
console.error( "time is not a valid number", this, i, currTime ); | |
valid = false; | |
break; | |
} | |
if( prevTime !== null && prevTime > currTime ) { | |
console.error( "out of order keys", this, i, currTime, prevTime ); | |
valid = false; | |
break; | |
} | |
prevTime = currTime; | |
} | |
if ( values !== undefined ) { | |
if ( THREE.AnimationUtils.isTypedArray( values ) ) { | |
for ( var i = 0, n = values.length; i !== n; ++ i ) { | |
var value = values[ i ]; | |
if ( isNaN( value ) ) { | |
console.error( "value is not a valid number", this, i, value ); | |
valid = false; | |
break; | |
} | |
} | |
} | |
} | |
return valid; | |
}, | |
// removes equivalent sequential keys as common in morph target sequences | |
// (0,0,0,0,1,1,1,0,0,0,0,0,0,0) --> (0,0,1,1,0,0) | |
optimize: function() { | |
var times = this.times, | |
values = this.values, | |
stride = this.getValueSize(), | |
writeIndex = 1; | |
for( var i = 1, n = times.length - 1; i <= n; ++ i ) { | |
var keep = false; | |
var time = times[ i ]; | |
var timeNext = times[ i + 1 ]; | |
// remove adjacent keyframes scheduled at the same time | |
if ( time !== timeNext && ( i !== 1 || time !== time[ 0 ] ) ) { | |
// remove unnecessary keyframes same as their neighbors | |
var offset = i * stride, | |
offsetP = offset - stride, | |
offsetN = offset + stride; | |
for ( var j = 0; j !== stride; ++ j ) { | |
var value = values[ offset + j ]; | |
if ( value !== values[ offsetP + j ] || | |
value !== values[ offsetN + j ] ) { | |
keep = true; | |
break; | |
} | |
} | |
} | |
// in-place compaction | |
if ( keep ) { | |
if ( i !== writeIndex ) { | |
times[ writeIndex ] = times[ i ]; | |
var readOffset = i * stride, | |
writeOffset = writeIndex * stride; | |
for ( var j = 0; j !== stride; ++ j ) { | |
values[ writeOffset + j ] = values[ readOffset + j ]; | |
} | |
} | |
++ writeIndex; | |
} | |
} | |
if ( writeIndex !== times.length ) { | |
this.times = THREE.AnimationUtils.arraySlice( times, 0, writeIndex ); | |
this.values = THREE.AnimationUtils.arraySlice( values, 0, writeIndex * stride ); | |
} | |
return this; | |
} | |
}; | |
// Static methods: | |
Object.assign( THREE.KeyframeTrack, { | |
// Serialization (in static context, because of constructor invocation | |
// and automatic invocation of .toJSON): | |
parse: function( json ) { | |
if( json.type === undefined ) { | |
throw new Error( "track type undefined, can not parse" ); | |
} | |
var trackType = THREE.KeyframeTrack._getTrackTypeForValueTypeName( json.type ); | |
if ( json.times === undefined ) { | |
console.warn( "legacy JSON format detected, converting" ); | |
var times = [], values = []; | |
THREE.AnimationUtils.flattenJSON( json.keys, times, values, 'value' ); | |
json.times = times; | |
json.values = values; | |
} | |
// derived classes can define a static parse method | |
if ( trackType.parse !== undefined ) { | |
return trackType.parse( json ); | |
} else { | |
// by default, we asssume a constructor compatible with the base | |
return new trackType( | |
json.name, json.times, json.values, json.interpolation ); | |
} | |
}, | |
toJSON: function( track ) { | |
var trackType = track.constructor; | |
var json; | |
// derived classes can define a static toJSON method | |
if ( trackType.toJSON !== undefined ) { | |
json = trackType.toJSON( track ); | |
} else { | |
// by default, we assume the data can be serialized as-is | |
json = { | |
'name': track.name, | |
'times': THREE.AnimationUtils.convertArray( track.times, Array ), | |
'values': THREE.AnimationUtils.convertArray( track.values, Array ) | |
}; | |
var interpolation = track.getInterpolation(); | |
if ( interpolation !== track.DefaultInterpolation ) { | |
json.interpolation = interpolation; | |
} | |
} | |
json.type = track.ValueTypeName; // mandatory | |
return json; | |
}, | |
_getTrackTypeForValueTypeName: function( typeName ) { | |
switch( typeName.toLowerCase() ) { | |
case "scalar": | |
case "double": | |
case "float": | |
case "number": | |
case "integer": | |
return THREE.NumberKeyframeTrack; | |
case "vector": | |
case "vector2": | |
case "vector3": | |
case "vector4": | |
return THREE.VectorKeyframeTrack; | |
case "color": | |
return THREE.ColorKeyframeTrack; | |
case "quaternion": | |
return THREE.QuaternionKeyframeTrack; | |
case "bool": | |
case "boolean": | |
return THREE.BooleanKeyframeTrack; | |
case "string": | |
return THREE.StringKeyframeTrack; | |
}; | |
throw new Error( "Unsupported typeName: " + typeName ); | |
} | |
} ); | |
// File:src/animation/PropertyBinding.js | |
/** | |
* | |
* A reference to a real property in the scene graph. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.PropertyBinding = function ( rootNode, path, parsedPath ) { | |
this.path = path; | |
this.parsedPath = parsedPath || | |
THREE.PropertyBinding.parseTrackName( path ); | |
this.node = THREE.PropertyBinding.findNode( | |
rootNode, this.parsedPath.nodeName ) || rootNode; | |
this.rootNode = rootNode; | |
}; | |
THREE.PropertyBinding.prototype = { | |
constructor: THREE.PropertyBinding, | |
getValue: function getValue_unbound( targetArray, offset ) { | |
this.bind(); | |
this.getValue( targetArray, offset ); | |
// Note: This class uses a State pattern on a per-method basis: | |
// 'bind' sets 'this.getValue' / 'setValue' and shadows the | |
// prototype version of these methods with one that represents | |
// the bound state. When the property is not found, the methods | |
// become no-ops. | |
}, | |
setValue: function getValue_unbound( sourceArray, offset ) { | |
this.bind(); | |
this.setValue( sourceArray, offset ); | |
}, | |
// create getter / setter pair for a property in the scene graph | |
bind: function() { | |
var targetObject = this.node, | |
parsedPath = this.parsedPath, | |
objectName = parsedPath.objectName, | |
propertyName = parsedPath.propertyName, | |
propertyIndex = parsedPath.propertyIndex; | |
if ( ! targetObject ) { | |
targetObject = THREE.PropertyBinding.findNode( | |
this.rootNode, parsedPath.nodeName ) || this.rootNode; | |
this.node = targetObject; | |
} | |
// set fail state so we can just 'return' on error | |
this.getValue = this._getValue_unavailable; | |
this.setValue = this._setValue_unavailable; | |
// ensure there is a value node | |
if ( ! targetObject ) { | |
console.error( " trying to update node for track: " + this.path + " but it wasn't found." ); | |
return; | |
} | |
if( objectName ) { | |
var objectIndex = parsedPath.objectIndex; | |
// special cases were we need to reach deeper into the hierarchy to get the face materials.... | |
switch ( objectName ) { | |
case 'materials': | |
if( ! targetObject.material ) { | |
console.error( ' can not bind to material as node does not have a material', this ); | |
return; | |
} | |
if( ! targetObject.material.materials ) { | |
console.error( ' can not bind to material.materials as node.material does not have a materials array', this ); | |
return; | |
} | |
targetObject = targetObject.material.materials; | |
break; | |
case 'bones': | |
if( ! targetObject.skeleton ) { | |
console.error( ' can not bind to bones as node does not have a skeleton', this ); | |
return; | |
} | |
// potential future optimization: skip this if propertyIndex is already an integer | |
// and convert the integer string to a true integer. | |
targetObject = targetObject.skeleton.bones; | |
// support resolving morphTarget names into indices. | |
for ( var i = 0; i < targetObject.length; i ++ ) { | |
if ( targetObject[i].name === objectIndex ) { | |
objectIndex = i; | |
break; | |
} | |
} | |
break; | |
default: | |
if ( targetObject[ objectName ] === undefined ) { | |
console.error( ' can not bind to objectName of node, undefined', this ); | |
return; | |
} | |
targetObject = targetObject[ objectName ]; | |
} | |
if ( objectIndex !== undefined ) { | |
if( targetObject[ objectIndex ] === undefined ) { | |
console.error( " trying to bind to objectIndex of objectName, but is undefined:", this, targetObject ); | |
return; | |
} | |
targetObject = targetObject[ objectIndex ]; | |
} | |
} | |
// resolve property | |
var nodeProperty = targetObject[ propertyName ]; | |
if ( ! nodeProperty ) { | |
var nodeName = parsedPath.nodeName; | |
console.error( " trying to update property for track: " + nodeName + | |
'.' + propertyName + " but it wasn't found.", targetObject ); | |
return; | |
} | |
// determine versioning scheme | |
var versioning = this.Versioning.None; | |
if ( targetObject.needsUpdate !== undefined ) { // material | |
versioning = this.Versioning.NeedsUpdate; | |
this.targetObject = targetObject; | |
} else if ( targetObject.matrixWorldNeedsUpdate !== undefined ) { // node transform | |
versioning = this.Versioning.MatrixWorldNeedsUpdate; | |
this.targetObject = targetObject; | |
} | |
// determine how the property gets bound | |
var bindingType = this.BindingType.Direct; | |
if ( propertyIndex !== undefined ) { | |
// access a sub element of the property array (only primitives are supported right now) | |
if ( propertyName === "morphTargetInfluences" ) { | |
// potential optimization, skip this if propertyIndex is already an integer, and convert the integer string to a true integer. | |
// support resolving morphTarget names into indices. | |
if ( ! targetObject.geometry ) { | |
console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry', this ); | |
return; | |
} | |
if ( ! targetObject.geometry.morphTargets ) { | |
console.error( ' can not bind to morphTargetInfluences becasuse node does not have a geometry.morphTargets', this ); | |
return; | |
} | |
for ( var i = 0; i < this.node.geometry.morphTargets.length; i ++ ) { | |
if ( targetObject.geometry.morphTargets[i].name === propertyIndex ) { | |
propertyIndex = i; | |
break; | |
} | |
} | |
} | |
bindingType = this.BindingType.ArrayElement; | |
this.resolvedProperty = nodeProperty; | |
this.propertyIndex = propertyIndex; | |
} else if ( nodeProperty.fromArray !== undefined && nodeProperty.toArray !== undefined ) { | |
// must use copy for Object3D.Euler/Quaternion | |
bindingType = this.BindingType.HasFromToArray; | |
this.resolvedProperty = nodeProperty; | |
} else if ( nodeProperty.length !== undefined ) { | |
bindingType = this.BindingType.EntireArray; | |
this.resolvedProperty = nodeProperty; | |
} else { | |
this.propertyName = propertyName; | |
} | |
// select getter / setter | |
this.getValue = this.GetterByBindingType[ bindingType ]; | |
this.setValue = this.SetterByBindingTypeAndVersioning[ bindingType ][ versioning ]; | |
}, | |
unbind: function() { | |
this.node = null; | |
// back to the prototype version of getValue / setValue | |
// note: avoiding to mutate the shape of 'this' via 'delete' | |
this.getValue = this._getValue_unbound; | |
this.setValue = this._setValue_unbound; | |
} | |
}; | |
Object.assign( THREE.PropertyBinding.prototype, { // prototype, continued | |
// these are used to "bind" a nonexistent property | |
_getValue_unavailable: function() {}, | |
_setValue_unavailable: function() {}, | |
// initial state of these methods that calls 'bind' | |
_getValue_unbound: THREE.PropertyBinding.prototype.getValue, | |
_setValue_unbound: THREE.PropertyBinding.prototype.setValue, | |
BindingType: { | |
Direct: 0, | |
EntireArray: 1, | |
ArrayElement: 2, | |
HasFromToArray: 3 | |
}, | |
Versioning: { | |
None: 0, | |
NeedsUpdate: 1, | |
MatrixWorldNeedsUpdate: 2 | |
}, | |
GetterByBindingType: [ | |
function getValue_direct( buffer, offset ) { | |
buffer[ offset ] = this.node[ this.propertyName ]; | |
}, | |
function getValue_array( buffer, offset ) { | |
var source = this.resolvedProperty; | |
for ( var i = 0, n = source.length; i !== n; ++ i ) { | |
buffer[ offset ++ ] = source[ i ]; | |
} | |
}, | |
function getValue_arrayElement( buffer, offset ) { | |
buffer[ offset ] = this.resolvedProperty[ this.propertyIndex ]; | |
}, | |
function getValue_toArray( buffer, offset ) { | |
this.resolvedProperty.toArray( buffer, offset ); | |
} | |
], | |
SetterByBindingTypeAndVersioning: [ | |
[ | |
// Direct | |
function setValue_direct( buffer, offset ) { | |
this.node[ this.propertyName ] = buffer[ offset ]; | |
}, | |
function setValue_direct_setNeedsUpdate( buffer, offset ) { | |
this.node[ this.propertyName ] = buffer[ offset ]; | |
this.targetObject.needsUpdate = true; | |
}, | |
function setValue_direct_setMatrixWorldNeedsUpdate( buffer, offset ) { | |
this.node[ this.propertyName ] = buffer[ offset ]; | |
this.targetObject.matrixWorldNeedsUpdate = true; | |
} | |
], [ | |
// EntireArray | |
function setValue_array( buffer, offset ) { | |
var dest = this.resolvedProperty; | |
for ( var i = 0, n = dest.length; i !== n; ++ i ) { | |
dest[ i ] = buffer[ offset ++ ]; | |
} | |
}, | |
function setValue_array_setNeedsUpdate( buffer, offset ) { | |
var dest = this.resolvedProperty; | |
for ( var i = 0, n = dest.length; i !== n; ++ i ) { | |
dest[ i ] = buffer[ offset ++ ]; | |
} | |
this.targetObject.needsUpdate = true; | |
}, | |
function setValue_array_setMatrixWorldNeedsUpdate( buffer, offset ) { | |
var dest = this.resolvedProperty; | |
for ( var i = 0, n = dest.length; i !== n; ++ i ) { | |
dest[ i ] = buffer[ offset ++ ]; | |
} | |
this.targetObject.matrixWorldNeedsUpdate = true; | |
} | |
], [ | |
// ArrayElement | |
function setValue_arrayElement( buffer, offset ) { | |
this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; | |
}, | |
function setValue_arrayElement_setNeedsUpdate( buffer, offset ) { | |
this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; | |
this.targetObject.needsUpdate = true; | |
}, | |
function setValue_arrayElement_setMatrixWorldNeedsUpdate( buffer, offset ) { | |
this.resolvedProperty[ this.propertyIndex ] = buffer[ offset ]; | |
this.targetObject.matrixWorldNeedsUpdate = true; | |
} | |
], [ | |
// HasToFromArray | |
function setValue_fromArray( buffer, offset ) { | |
this.resolvedProperty.fromArray( buffer, offset ); | |
}, | |
function setValue_fromArray_setNeedsUpdate( buffer, offset ) { | |
this.resolvedProperty.fromArray( buffer, offset ); | |
this.targetObject.needsUpdate = true; | |
}, | |
function setValue_fromArray_setMatrixWorldNeedsUpdate( buffer, offset ) { | |
this.resolvedProperty.fromArray( buffer, offset ); | |
this.targetObject.matrixWorldNeedsUpdate = true; | |
} | |
] | |
] | |
} ); | |
THREE.PropertyBinding.Composite = | |
function( targetGroup, path, optionalParsedPath ) { | |
var parsedPath = optionalParsedPath || | |
THREE.PropertyBinding.parseTrackName( path ); | |
this._targetGroup = targetGroup; | |
this._bindings = targetGroup.subscribe_( path, parsedPath ); | |
}; | |
THREE.PropertyBinding.Composite.prototype = { | |
constructor: THREE.PropertyBinding.Composite, | |
getValue: function( array, offset ) { | |
this.bind(); // bind all binding | |
var firstValidIndex = this._targetGroup.nCachedObjects_, | |
binding = this._bindings[ firstValidIndex ]; | |
// and only call .getValue on the first | |
if ( binding !== undefined ) binding.getValue( array, offset ); | |
}, | |
setValue: function( array, offset ) { | |
var bindings = this._bindings; | |
for ( var i = this._targetGroup.nCachedObjects_, | |
n = bindings.length; i !== n; ++ i ) { | |
bindings[ i ].setValue( array, offset ); | |
} | |
}, | |
bind: function() { | |
var bindings = this._bindings; | |
for ( var i = this._targetGroup.nCachedObjects_, | |
n = bindings.length; i !== n; ++ i ) { | |
bindings[ i ].bind(); | |
} | |
}, | |
unbind: function() { | |
var bindings = this._bindings; | |
for ( var i = this._targetGroup.nCachedObjects_, | |
n = bindings.length; i !== n; ++ i ) { | |
bindings[ i ].unbind(); | |
} | |
} | |
}; | |
THREE.PropertyBinding.create = function( root, path, parsedPath ) { | |
if ( ! ( root instanceof THREE.AnimationObjectGroup ) ) { | |
return new THREE.PropertyBinding( root, path, parsedPath ); | |
} else { | |
return new THREE.PropertyBinding.Composite( root, path, parsedPath ); | |
} | |
}; | |
THREE.PropertyBinding.parseTrackName = function( trackName ) { | |
// matches strings in the form of: | |
// nodeName.property | |
// nodeName.property[accessor] | |
// nodeName.material.property[accessor] | |
// uuid.property[accessor] | |
// uuid.objectName[objectIndex].propertyName[propertyIndex] | |
// parentName/nodeName.property | |
// parentName/parentName/nodeName.property[index] | |
// .bone[Armature.DEF_cog].position | |
// created and tested via https://regex101.com/#javascript | |
var re = /^(([\w]+\/)*)([\w-\d]+)?(\.([\w]+)(\[([\w\d\[\]\_. ]+)\])?)?(\.([\w.]+)(\[([\w\d\[\]\_. ]+)\])?)$/; | |
var matches = re.exec(trackName); | |
if( ! matches ) { | |
throw new Error( "cannot parse trackName at all: " + trackName ); | |
} | |
if (matches.index === re.lastIndex) { | |
re.lastIndex++; | |
} | |
var results = { | |
// directoryName: matches[1], // (tschw) currently unused | |
nodeName: matches[3], // allowed to be null, specified root node. | |
objectName: matches[5], | |
objectIndex: matches[7], | |
propertyName: matches[9], | |
propertyIndex: matches[11] // allowed to be null, specifies that the whole property is set. | |
}; | |
if( results.propertyName === null || results.propertyName.length === 0 ) { | |
throw new Error( "can not parse propertyName from trackName: " + trackName ); | |
} | |
return results; | |
}; | |
THREE.PropertyBinding.findNode = function( root, nodeName ) { | |
if( ! nodeName || nodeName === "" || nodeName === "root" || nodeName === "." || nodeName === -1 || nodeName === root.name || nodeName === root.uuid ) { | |
return root; | |
} | |
// search into skeleton bones. | |
if( root.skeleton ) { | |
var searchSkeleton = function( skeleton ) { | |
for( var i = 0; i < skeleton.bones.length; i ++ ) { | |
var bone = skeleton.bones[i]; | |
if( bone.name === nodeName ) { | |
return bone; | |
} | |
} | |
return null; | |
}; | |
var bone = searchSkeleton( root.skeleton ); | |
if( bone ) { | |
return bone; | |
} | |
} | |
// search into node subtree. | |
if( root.children ) { | |
var searchNodeSubtree = function( children ) { | |
for( var i = 0; i < children.length; i ++ ) { | |
var childNode = children[i]; | |
if( childNode.name === nodeName || childNode.uuid === nodeName ) { | |
return childNode; | |
} | |
var result = searchNodeSubtree( childNode.children ); | |
if( result ) return result; | |
} | |
return null; | |
}; | |
var subTreeNode = searchNodeSubtree( root.children ); | |
if( subTreeNode ) { | |
return subTreeNode; | |
} | |
} | |
return null; | |
} | |
// File:src/animation/PropertyMixer.js | |
/** | |
* | |
* Buffered scene graph property that allows weighted accumulation. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.PropertyMixer = function ( binding, typeName, valueSize ) { | |
this.binding = binding; | |
this.valueSize = valueSize; | |
var bufferType = Float64Array, | |
mixFunction; | |
switch ( typeName ) { | |
case 'quaternion': mixFunction = this._slerp; break; | |
case 'string': | |
case 'bool': | |
bufferType = Array, mixFunction = this._select; break; | |
default: mixFunction = this._lerp; | |
} | |
this.buffer = new bufferType( valueSize * 4 ); | |
// layout: [ incoming | accu0 | accu1 | orig ] | |
// | |
// interpolators can use .buffer as their .result | |
// the data then goes to 'incoming' | |
// | |
// 'accu0' and 'accu1' are used frame-interleaved for | |
// the cumulative result and are compared to detect | |
// changes | |
// | |
// 'orig' stores the original state of the property | |
this._mixBufferRegion = mixFunction; | |
this.cumulativeWeight = 0; | |
this.useCount = 0; | |
this.referenceCount = 0; | |
}; | |
THREE.PropertyMixer.prototype = { | |
constructor: THREE.PropertyMixer, | |
// accumulate data in the 'incoming' region into 'accu<i>' | |
accumulate: function( accuIndex, weight ) { | |
// note: happily accumulating nothing when weight = 0, the caller knows | |
// the weight and shouldn't have made the call in the first place | |
var buffer = this.buffer, | |
stride = this.valueSize, | |
offset = accuIndex * stride + stride, | |
currentWeight = this.cumulativeWeight; | |
if ( currentWeight === 0 ) { | |
// accuN := incoming * weight | |
for ( var i = 0; i !== stride; ++ i ) { | |
buffer[ offset + i ] = buffer[ i ]; | |
} | |
currentWeight = weight; | |
} else { | |
// accuN := accuN + incoming * weight | |
currentWeight += weight; | |
var mix = weight / currentWeight; | |
this._mixBufferRegion( buffer, offset, 0, mix, stride ); | |
} | |
this.cumulativeWeight = currentWeight; | |
}, | |
// apply the state of 'accu<i>' to the binding when accus differ | |
apply: function( accuIndex ) { | |
var stride = this.valueSize, | |
buffer = this.buffer, | |
offset = accuIndex * stride + stride, | |
weight = this.cumulativeWeight, | |
binding = this.binding; | |
this.cumulativeWeight = 0; | |
if ( weight < 1 ) { | |
// accuN := accuN + original * ( 1 - cumulativeWeight ) | |
var originalValueOffset = stride * 3; | |
this._mixBufferRegion( | |
buffer, offset, originalValueOffset, 1 - weight, stride ); | |
} | |
for ( var i = stride, e = stride + stride; i !== e; ++ i ) { | |
if ( buffer[ i ] !== buffer[ i + stride ] ) { | |
// value has changed -> update scene graph | |
binding.setValue( buffer, offset ); | |
break; | |
} | |
} | |
}, | |
// remember the state of the bound property and copy it to both accus | |
saveOriginalState: function() { | |
var binding = this.binding; | |
var buffer = this.buffer, | |
stride = this.valueSize, | |
originalValueOffset = stride * 3; | |
binding.getValue( buffer, originalValueOffset ); | |
// accu[0..1] := orig -- initially detect changes against the original | |
for ( var i = stride, e = originalValueOffset; i !== e; ++ i ) { | |
buffer[ i ] = buffer[ originalValueOffset + ( i % stride ) ]; | |
} | |
this.cumulativeWeight = 0; | |
}, | |
// apply the state previously taken via 'saveOriginalState' to the binding | |
restoreOriginalState: function() { | |
var originalValueOffset = this.valueSize * 3; | |
this.binding.setValue( this.buffer, originalValueOffset ); | |
}, | |
// mix functions | |
_select: function( buffer, dstOffset, srcOffset, t, stride ) { | |
if ( t >= 0.5 ) { | |
for ( var i = 0; i !== stride; ++ i ) { | |
buffer[ dstOffset + i ] = buffer[ srcOffset + i ]; | |
} | |
} | |
}, | |
_slerp: function( buffer, dstOffset, srcOffset, t, stride ) { | |
THREE.Quaternion.slerpFlat( buffer, dstOffset, | |
buffer, dstOffset, buffer, srcOffset, t ); | |
}, | |
_lerp: function( buffer, dstOffset, srcOffset, t, stride ) { | |
var s = 1 - t; | |
for ( var i = 0; i !== stride; ++ i ) { | |
var j = dstOffset + i; | |
buffer[ j ] = buffer[ j ] * s + buffer[ srcOffset + i ] * t; | |
} | |
} | |
}; | |
// File:src/animation/tracks/BooleanKeyframeTrack.js | |
/** | |
* | |
* A Track of Boolean keyframe values. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.BooleanKeyframeTrack = function ( name, times, values ) { | |
THREE.KeyframeTrack.call( this, name, times, values ); | |
}; | |
THREE.BooleanKeyframeTrack.prototype = | |
Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { | |
constructor: THREE.BooleanKeyframeTrack, | |
ValueTypeName: 'bool', | |
ValueBufferType: Array, | |
DefaultInterpolation: THREE.IntepolateDiscrete, | |
InterpolantFactoryMethodLinear: undefined, | |
InterpolantFactoryMethodSmooth: undefined | |
// Note: Actually this track could have a optimized / compressed | |
// representation of a single value and a custom interpolant that | |
// computes "firstValue ^ isOdd( index )". | |
} ); | |
// File:src/animation/tracks/NumberKeyframeTrack.js | |
/** | |
* | |
* A Track of numeric keyframe values. | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.NumberKeyframeTrack = function ( name, times, values, interpolation ) { | |
THREE.KeyframeTrack.call( this, name, times, values, interpolation ); | |
}; | |
THREE.NumberKeyframeTrack.prototype = | |
Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { | |
constructor: THREE.NumberKeyframeTrack, | |
ValueTypeName: 'number', | |
// ValueBufferType is inherited | |
// DefaultInterpolation is inherited | |
} ); | |
// File:src/animation/tracks/QuaternionKeyframeTrack.js | |
/** | |
* | |
* A Track of quaternion keyframe values. | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.QuaternionKeyframeTrack = function ( name, times, values, interpolation ) { | |
THREE.KeyframeTrack.call( this, name, times, values, interpolation ); | |
}; | |
THREE.QuaternionKeyframeTrack.prototype = | |
Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { | |
constructor: THREE.QuaternionKeyframeTrack, | |
ValueTypeName: 'quaternion', | |
// ValueBufferType is inherited | |
DefaultInterpolation: THREE.InterpolateLinear, | |
InterpolantFactoryMethodLinear: function( result ) { | |
return new THREE.QuaternionLinearInterpolant( | |
this.times, this.values, this.getValueSize(), result ); | |
}, | |
InterpolantFactoryMethodSmooth: undefined // not yet implemented | |
} ); | |
// File:src/animation/tracks/StringKeyframeTrack.js | |
/** | |
* | |
* A Track that interpolates Strings | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.StringKeyframeTrack = function ( name, times, values, interpolation ) { | |
THREE.KeyframeTrack.call( this, name, times, values, interpolation ); | |
}; | |
THREE.StringKeyframeTrack.prototype = | |
Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { | |
constructor: THREE.StringKeyframeTrack, | |
ValueTypeName: 'string', | |
ValueBufferType: Array, | |
DefaultInterpolation: THREE.IntepolateDiscrete, | |
InterpolantFactoryMethodLinear: undefined, | |
InterpolantFactoryMethodSmooth: undefined | |
} ); | |
// File:src/animation/tracks/VectorKeyframeTrack.js | |
/** | |
* | |
* A Track of vectored keyframe values. | |
* | |
* | |
* @author Ben Houston / http://clara.io/ | |
* @author David Sarno / http://lighthaus.us/ | |
* @author tschw | |
*/ | |
THREE.VectorKeyframeTrack = function ( name, times, values, interpolation ) { | |
THREE.KeyframeTrack.call( this, name, times, values, interpolation ); | |
}; | |
THREE.VectorKeyframeTrack.prototype = | |
Object.assign( Object.create( THREE.KeyframeTrack.prototype ), { | |
constructor: THREE.VectorKeyframeTrack, | |
ValueTypeName: 'vector' | |
// ValueBufferType is inherited | |
// DefaultInterpolation is inherited | |
} ); | |
// File:src/audio/Audio.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Audio = function ( listener ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Audio'; | |
this.context = listener.context; | |
this.source = this.context.createBufferSource(); | |
this.source.onended = this.onEnded.bind( this ); | |
this.gain = this.context.createGain(); | |
this.gain.connect( listener.getInput() ); | |
this.autoplay = false; | |
this.startTime = 0; | |
this.playbackRate = 1; | |
this.isPlaying = false; | |
this.hasPlaybackControl = true; | |
this.sourceType = 'empty'; | |
this.filter = null; | |
}; | |
THREE.Audio.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Audio.prototype.constructor = THREE.Audio; | |
THREE.Audio.prototype.getOutput = function () { | |
return this.gain; | |
}; | |
THREE.Audio.prototype.load = function ( file ) { | |
var buffer = new THREE.AudioBuffer( this.context ); | |
buffer.load( file ); | |
this.setBuffer( buffer ); | |
return this; | |
}; | |
THREE.Audio.prototype.setNodeSource = function ( audioNode ) { | |
this.hasPlaybackControl = false; | |
this.sourceType = 'audioNode'; | |
this.source = audioNode; | |
this.connect(); | |
return this; | |
}; | |
THREE.Audio.prototype.setBuffer = function ( audioBuffer ) { | |
var scope = this; | |
audioBuffer.onReady( function( buffer ) { | |
scope.source.buffer = buffer; | |
scope.sourceType = 'buffer'; | |
if ( scope.autoplay ) scope.play(); | |
} ); | |
return this; | |
}; | |
THREE.Audio.prototype.play = function () { | |
if ( this.isPlaying === true ) { | |
console.warn( 'THREE.Audio: Audio is already playing.' ); | |
return; | |
} | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return; | |
} | |
var source = this.context.createBufferSource(); | |
source.buffer = this.source.buffer; | |
source.loop = this.source.loop; | |
source.onended = this.source.onended; | |
source.start( 0, this.startTime ); | |
source.playbackRate.value = this.playbackRate; | |
this.isPlaying = true; | |
this.source = source; | |
this.connect(); | |
}; | |
THREE.Audio.prototype.pause = function () { | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return; | |
} | |
this.source.stop(); | |
this.startTime = this.context.currentTime; | |
}; | |
THREE.Audio.prototype.stop = function () { | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return; | |
} | |
this.source.stop(); | |
this.startTime = 0; | |
}; | |
THREE.Audio.prototype.connect = function () { | |
if ( this.filter !== null ) { | |
this.source.connect( this.filter ); | |
this.filter.connect( this.getOutput() ); | |
} else { | |
this.source.connect( this.getOutput() ); | |
} | |
}; | |
THREE.Audio.prototype.disconnect = function () { | |
if ( this.filter !== null ) { | |
this.source.disconnect( this.filter ); | |
this.filter.disconnect( this.getOutput() ); | |
} else { | |
this.source.disconnect( this.getOutput() ); | |
} | |
}; | |
THREE.Audio.prototype.getFilter = function () { | |
return this.filter; | |
}; | |
THREE.Audio.prototype.setFilter = function ( value ) { | |
if ( value === undefined ) value = null; | |
if ( this.isPlaying === true ) { | |
this.disconnect(); | |
this.filter = value; | |
this.connect(); | |
} else { | |
this.filter = value; | |
} | |
}; | |
THREE.Audio.prototype.setPlaybackRate = function ( value ) { | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return; | |
} | |
this.playbackRate = value; | |
if ( this.isPlaying === true ) { | |
this.source.playbackRate.value = this.playbackRate; | |
} | |
}; | |
THREE.Audio.prototype.getPlaybackRate = function () { | |
return this.playbackRate; | |
}; | |
THREE.Audio.prototype.onEnded = function() { | |
this.isPlaying = false; | |
}; | |
THREE.Audio.prototype.setLoop = function ( value ) { | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return; | |
} | |
this.source.loop = value; | |
}; | |
THREE.Audio.prototype.getLoop = function () { | |
if ( this.hasPlaybackControl === false ) { | |
console.warn( 'THREE.Audio: this Audio has no playback control.' ); | |
return false; | |
} | |
return this.source.loop; | |
}; | |
THREE.Audio.prototype.setVolume = function ( value ) { | |
this.gain.gain.value = value; | |
}; | |
THREE.Audio.prototype.getVolume = function () { | |
return this.gain.gain.value; | |
}; | |
// File:src/audio/AudioAnalyser.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.AudioAnalyser = function ( audio, fftSize ) { | |
this.analyser = audio.context.createAnalyser(); | |
this.analyser.fftSize = fftSize !== undefined ? fftSize : 2048; | |
this.data = new Uint8Array( this.analyser.frequencyBinCount ); | |
audio.getOutput().connect( this.analyser ); | |
}; | |
THREE.AudioAnalyser.prototype = { | |
constructor: THREE.AudioAnalyser, | |
getData: function () { | |
this.analyser.getByteFrequencyData( this.data ); | |
return this.data; | |
} | |
}; | |
// File:src/audio/AudioBuffer.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.AudioBuffer = function ( context ) { | |
this.context = context; | |
this.ready = false; | |
this.readyCallbacks = []; | |
}; | |
THREE.AudioBuffer.prototype.load = function ( file ) { | |
var scope = this; | |
var request = new XMLHttpRequest(); | |
request.open( 'GET', file, true ); | |
request.responseType = 'arraybuffer'; | |
request.onload = function ( e ) { | |
scope.context.decodeAudioData( this.response, function ( buffer ) { | |
scope.buffer = buffer; | |
scope.ready = true; | |
for ( var i = 0; i < scope.readyCallbacks.length; i ++ ) { | |
scope.readyCallbacks[ i ]( scope.buffer ); | |
} | |
scope.readyCallbacks = []; | |
} ); | |
}; | |
request.send(); | |
return this; | |
}; | |
THREE.AudioBuffer.prototype.onReady = function ( callback ) { | |
if ( this.ready ) { | |
callback( this.buffer ); | |
} else { | |
this.readyCallbacks.push( callback ); | |
} | |
}; | |
// File:src/audio/PositionalAudio.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.PositionalAudio = function ( listener ) { | |
THREE.Audio.call( this, listener ); | |
this.panner = this.context.createPanner(); | |
this.panner.connect( this.gain ); | |
}; | |
THREE.PositionalAudio.prototype = Object.create( THREE.Audio.prototype ); | |
THREE.PositionalAudio.prototype.constructor = THREE.PositionalAudio; | |
THREE.PositionalAudio.prototype.getOutput = function () { | |
return this.panner; | |
}; | |
THREE.PositionalAudio.prototype.setRefDistance = function ( value ) { | |
this.panner.refDistance = value; | |
}; | |
THREE.PositionalAudio.prototype.getRefDistance = function () { | |
return this.panner.refDistance; | |
}; | |
THREE.PositionalAudio.prototype.setRolloffFactor = function ( value ) { | |
this.panner.rolloffFactor = value; | |
}; | |
THREE.PositionalAudio.prototype.getRolloffFactor = function () { | |
return this.panner.rolloffFactor; | |
}; | |
THREE.PositionalAudio.prototype.setDistanceModel = function ( value ) { | |
this.panner.distanceModel = value; | |
}; | |
THREE.PositionalAudio.prototype.getDistanceModel = function () { | |
return this.panner.distanceModel; | |
}; | |
THREE.PositionalAudio.prototype.setMaxDistance = function ( value ) { | |
this.panner.maxDistance = value; | |
}; | |
THREE.PositionalAudio.prototype.getMaxDistance = function () { | |
return this.panner.maxDistance; | |
}; | |
THREE.PositionalAudio.prototype.updateMatrixWorld = ( function () { | |
var position = new THREE.Vector3(); | |
return function updateMatrixWorld( force ) { | |
THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); | |
position.setFromMatrixPosition( this.matrixWorld ); | |
this.panner.setPosition( position.x, position.y, position.z ); | |
}; | |
} )(); | |
// File:src/audio/AudioListener.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.AudioListener = function () { | |
THREE.Object3D.call( this ); | |
this.type = 'AudioListener'; | |
this.context = new ( window.AudioContext || window.webkitAudioContext )(); | |
this.gain = this.context.createGain(); | |
this.gain.connect( this.context.destination ); | |
this.filter = null; | |
}; | |
THREE.AudioListener.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.AudioListener.prototype.constructor = THREE.AudioListener; | |
THREE.AudioListener.prototype.getInput = function () { | |
return this.gain; | |
}; | |
THREE.AudioListener.prototype.removeFilter = function ( ) { | |
if ( this.filter !== null ) { | |
this.gain.disconnect( this.filter ); | |
this.filter.disconnect( this.context.destination ); | |
this.gain.connect( this.context.destination ); | |
this.filter = null; | |
} | |
}; | |
THREE.AudioListener.prototype.setFilter = function ( value ) { | |
if ( this.filter !== null ) { | |
this.gain.disconnect( this.filter ); | |
this.filter.disconnect( this.context.destination ); | |
} else { | |
this.gain.disconnect( this.context.destination ); | |
} | |
this.filter = value; | |
this.gain.connect( this.filter ); | |
this.filter.connect( this.context.destination ); | |
}; | |
THREE.AudioListener.prototype.getFilter = function () { | |
return this.filter; | |
}; | |
THREE.AudioListener.prototype.setMasterVolume = function ( value ) { | |
this.gain.gain.value = value; | |
}; | |
THREE.AudioListener.prototype.getMasterVolume = function () { | |
return this.gain.gain.value; | |
}; | |
THREE.AudioListener.prototype.updateMatrixWorld = ( function () { | |
var position = new THREE.Vector3(); | |
var quaternion = new THREE.Quaternion(); | |
var scale = new THREE.Vector3(); | |
var orientation = new THREE.Vector3(); | |
return function updateMatrixWorld( force ) { | |
THREE.Object3D.prototype.updateMatrixWorld.call( this, force ); | |
var listener = this.context.listener; | |
var up = this.up; | |
this.matrixWorld.decompose( position, quaternion, scale ); | |
orientation.set( 0, 0, - 1 ).applyQuaternion( quaternion ); | |
listener.setPosition( position.x, position.y, position.z ); | |
listener.setOrientation( orientation.x, orientation.y, orientation.z, up.x, up.y, up.z ); | |
}; | |
} )(); | |
// File:src/cameras/Camera.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author WestLangley / http://github.com/WestLangley | |
*/ | |
THREE.Camera = function () { | |
THREE.Object3D.call( this ); | |
this.type = 'Camera'; | |
this.matrixWorldInverse = new THREE.Matrix4(); | |
this.projectionMatrix = new THREE.Matrix4(); | |
}; | |
THREE.Camera.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Camera.prototype.constructor = THREE.Camera; | |
THREE.Camera.prototype.getWorldDirection = function () { | |
var quaternion = new THREE.Quaternion(); | |
return function ( optionalTarget ) { | |
var result = optionalTarget || new THREE.Vector3(); | |
this.getWorldQuaternion( quaternion ); | |
return result.set( 0, 0, - 1 ).applyQuaternion( quaternion ); | |
}; | |
}(); | |
THREE.Camera.prototype.lookAt = function () { | |
// This routine does not support cameras with rotated and/or translated parent(s) | |
var m1 = new THREE.Matrix4(); | |
return function ( vector ) { | |
m1.lookAt( this.position, vector, this.up ); | |
this.quaternion.setFromRotationMatrix( m1 ); | |
}; | |
}(); | |
THREE.Camera.prototype.clone = function () { | |
return new this.constructor().copy( this ); | |
}; | |
THREE.Camera.prototype.copy = function ( source ) { | |
THREE.Object3D.prototype.copy.call( this, source ); | |
this.matrixWorldInverse.copy( source.matrixWorldInverse ); | |
this.projectionMatrix.copy( source.projectionMatrix ); | |
return this; | |
}; | |
// File:src/cameras/CubeCamera.js | |
/** | |
* Camera for rendering cube maps | |
* - renders scene into axis-aligned cube | |
* | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.CubeCamera = function ( near, far, cubeResolution ) { | |
THREE.Object3D.call( this ); | |
this.type = 'CubeCamera'; | |
var fov = 90, aspect = 1; | |
var cameraPX = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraPX.up.set( 0, - 1, 0 ); | |
cameraPX.lookAt( new THREE.Vector3( 1, 0, 0 ) ); | |
this.add( cameraPX ); | |
var cameraNX = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraNX.up.set( 0, - 1, 0 ); | |
cameraNX.lookAt( new THREE.Vector3( - 1, 0, 0 ) ); | |
this.add( cameraNX ); | |
var cameraPY = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraPY.up.set( 0, 0, 1 ); | |
cameraPY.lookAt( new THREE.Vector3( 0, 1, 0 ) ); | |
this.add( cameraPY ); | |
var cameraNY = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraNY.up.set( 0, 0, - 1 ); | |
cameraNY.lookAt( new THREE.Vector3( 0, - 1, 0 ) ); | |
this.add( cameraNY ); | |
var cameraPZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraPZ.up.set( 0, - 1, 0 ); | |
cameraPZ.lookAt( new THREE.Vector3( 0, 0, 1 ) ); | |
this.add( cameraPZ ); | |
var cameraNZ = new THREE.PerspectiveCamera( fov, aspect, near, far ); | |
cameraNZ.up.set( 0, - 1, 0 ); | |
cameraNZ.lookAt( new THREE.Vector3( 0, 0, - 1 ) ); | |
this.add( cameraNZ ); | |
var options = { format: THREE.RGBFormat, magFilter: THREE.LinearFilter, minFilter: THREE.LinearFilter }; | |
this.renderTarget = new THREE.WebGLRenderTargetCube( cubeResolution, cubeResolution, options ); | |
this.updateCubeMap = function ( renderer, scene ) { | |
if ( this.parent === null ) this.updateMatrixWorld(); | |
var renderTarget = this.renderTarget; | |
var generateMipmaps = renderTarget.texture.generateMipmaps; | |
renderTarget.texture.generateMipmaps = false; | |
renderTarget.activeCubeFace = 0; | |
renderer.render( scene, cameraPX, renderTarget ); | |
renderTarget.activeCubeFace = 1; | |
renderer.render( scene, cameraNX, renderTarget ); | |
renderTarget.activeCubeFace = 2; | |
renderer.render( scene, cameraPY, renderTarget ); | |
renderTarget.activeCubeFace = 3; | |
renderer.render( scene, cameraNY, renderTarget ); | |
renderTarget.activeCubeFace = 4; | |
renderer.render( scene, cameraPZ, renderTarget ); | |
renderTarget.texture.generateMipmaps = generateMipmaps; | |
renderTarget.activeCubeFace = 5; | |
renderer.render( scene, cameraNZ, renderTarget ); | |
renderer.setRenderTarget( null ); | |
}; | |
}; | |
THREE.CubeCamera.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.CubeCamera.prototype.constructor = THREE.CubeCamera; | |
// File:src/cameras/OrthographicCamera.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.OrthographicCamera = function ( left, right, top, bottom, near, far ) { | |
THREE.Camera.call( this ); | |
this.type = 'OrthographicCamera'; | |
this.zoom = 1; | |
this.left = left; | |
this.right = right; | |
this.top = top; | |
this.bottom = bottom; | |
this.near = ( near !== undefined ) ? near : 0.1; | |
this.far = ( far !== undefined ) ? far : 2000; | |
this.updateProjectionMatrix(); | |
}; | |
THREE.OrthographicCamera.prototype = Object.create( THREE.Camera.prototype ); | |
THREE.OrthographicCamera.prototype.constructor = THREE.OrthographicCamera; | |
THREE.OrthographicCamera.prototype.updateProjectionMatrix = function () { | |
var dx = ( this.right - this.left ) / ( 2 * this.zoom ); | |
var dy = ( this.top - this.bottom ) / ( 2 * this.zoom ); | |
var cx = ( this.right + this.left ) / 2; | |
var cy = ( this.top + this.bottom ) / 2; | |
this.projectionMatrix.makeOrthographic( cx - dx, cx + dx, cy + dy, cy - dy, this.near, this.far ); | |
}; | |
THREE.OrthographicCamera.prototype.copy = function ( source ) { | |
THREE.Camera.prototype.copy.call( this, source ); | |
this.left = source.left; | |
this.right = source.right; | |
this.top = source.top; | |
this.bottom = source.bottom; | |
this.near = source.near; | |
this.far = source.far; | |
this.zoom = source.zoom; | |
return this; | |
}; | |
THREE.OrthographicCamera.prototype.toJSON = function ( meta ) { | |
var data = THREE.Object3D.prototype.toJSON.call( this, meta ); | |
data.object.zoom = this.zoom; | |
data.object.left = this.left; | |
data.object.right = this.right; | |
data.object.top = this.top; | |
data.object.bottom = this.bottom; | |
data.object.near = this.near; | |
data.object.far = this.far; | |
return data; | |
}; | |
// File:src/cameras/PerspectiveCamera.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author greggman / http://games.greggman.com/ | |
* @author zz85 / http://www.lab4games.net/zz85/blog | |
*/ | |
THREE.PerspectiveCamera = function ( fov, aspect, near, far ) { | |
THREE.Camera.call( this ); | |
this.type = 'PerspectiveCamera'; | |
this.focalLength = 10; | |
this.zoom = 1; | |
this.fov = fov !== undefined ? fov : 50; | |
this.aspect = aspect !== undefined ? aspect : 1; | |
this.near = near !== undefined ? near : 0.1; | |
this.far = far !== undefined ? far : 2000; | |
this.updateProjectionMatrix(); | |
}; | |
THREE.PerspectiveCamera.prototype = Object.create( THREE.Camera.prototype ); | |
THREE.PerspectiveCamera.prototype.constructor = THREE.PerspectiveCamera; | |
/** | |
* Uses Focal Length (in mm) to estimate and set FOV | |
* 35mm (full-frame) camera is used if frame size is not specified; | |
* Formula based on http://www.bobatkins.com/photography/technical/field_of_view.html | |
*/ | |
THREE.PerspectiveCamera.prototype.setLens = function ( focalLength, frameHeight ) { | |
if ( frameHeight === undefined ) frameHeight = 24; | |
this.fov = 2 * THREE.Math.radToDeg( Math.atan( frameHeight / ( focalLength * 2 ) ) ); | |
this.updateProjectionMatrix(); | |
}; | |
/** | |
* Sets an offset in a larger frustum. This is useful for multi-window or | |
* multi-monitor/multi-machine setups. | |
* | |
* For example, if you have 3x2 monitors and each monitor is 1920x1080 and | |
* the monitors are in grid like this | |
* | |
* +---+---+---+ | |
* | A | B | C | | |
* +---+---+---+ | |
* | D | E | F | | |
* +---+---+---+ | |
* | |
* then for each monitor you would call it like this | |
* | |
* var w = 1920; | |
* var h = 1080; | |
* var fullWidth = w * 3; | |
* var fullHeight = h * 2; | |
* | |
* --A-- | |
* camera.setOffset( fullWidth, fullHeight, w * 0, h * 0, w, h ); | |
* --B-- | |
* camera.setOffset( fullWidth, fullHeight, w * 1, h * 0, w, h ); | |
* --C-- | |
* camera.setOffset( fullWidth, fullHeight, w * 2, h * 0, w, h ); | |
* --D-- | |
* camera.setOffset( fullWidth, fullHeight, w * 0, h * 1, w, h ); | |
* --E-- | |
* camera.setOffset( fullWidth, fullHeight, w * 1, h * 1, w, h ); | |
* --F-- | |
* camera.setOffset( fullWidth, fullHeight, w * 2, h * 1, w, h ); | |
* | |
* Note there is no reason monitors have to be the same size or in a grid. | |
*/ | |
THREE.PerspectiveCamera.prototype.setViewOffset = function ( fullWidth, fullHeight, x, y, width, height ) { | |
this.fullWidth = fullWidth; | |
this.fullHeight = fullHeight; | |
this.x = x; | |
this.y = y; | |
this.width = width; | |
this.height = height; | |
this.updateProjectionMatrix(); | |
}; | |
THREE.PerspectiveCamera.prototype.updateProjectionMatrix = function () { | |
var fov = THREE.Math.radToDeg( 2 * Math.atan( Math.tan( THREE.Math.degToRad( this.fov ) * 0.5 ) / this.zoom ) ); | |
if ( this.fullWidth ) { | |
var aspect = this.fullWidth / this.fullHeight; | |
var top = Math.tan( THREE.Math.degToRad( fov * 0.5 ) ) * this.near; | |
var bottom = - top; | |
var left = aspect * bottom; | |
var right = aspect * top; | |
var width = Math.abs( right - left ); | |
var height = Math.abs( top - bottom ); | |
this.projectionMatrix.makeFrustum( | |
left + this.x * width / this.fullWidth, | |
left + ( this.x + this.width ) * width / this.fullWidth, | |
top - ( this.y + this.height ) * height / this.fullHeight, | |
top - this.y * height / this.fullHeight, | |
this.near, | |
this.far | |
); | |
} else { | |
this.projectionMatrix.makePerspective( fov, this.aspect, this.near, this.far ); | |
} | |
}; | |
THREE.PerspectiveCamera.prototype.copy = function ( source ) { | |
THREE.Camera.prototype.copy.call( this, source ); | |
this.focalLength = source.focalLength; | |
this.zoom = source.zoom; | |
this.fov = source.fov; | |
this.aspect = source.aspect; | |
this.near = source.near; | |
this.far = source.far; | |
return this; | |
}; | |
THREE.PerspectiveCamera.prototype.toJSON = function ( meta ) { | |
var data = THREE.Object3D.prototype.toJSON.call( this, meta ); | |
data.object.focalLength = this.focalLength; | |
data.object.zoom = this.zoom; | |
data.object.fov = this.fov; | |
data.object.aspect = this.aspect; | |
data.object.near = this.near; | |
data.object.far = this.far; | |
return data; | |
}; | |
// File:src/cameras/StereoCamera.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.StereoCamera = function () { | |
this.type = 'StereoCamera'; | |
this.aspect = 1; | |
this.cameraL = new THREE.PerspectiveCamera(); | |
this.cameraL.layers.enable( 1 ); | |
this.cameraL.matrixAutoUpdate = false; | |
this.cameraR = new THREE.PerspectiveCamera(); | |
this.cameraR.layers.enable( 2 ); | |
this.cameraR.matrixAutoUpdate = false; | |
}; | |
THREE.StereoCamera.prototype = { | |
constructor: THREE.StereoCamera, | |
update: ( function () { | |
var focalLength, fov, aspect, near, far; | |
var eyeRight = new THREE.Matrix4(); | |
var eyeLeft = new THREE.Matrix4(); | |
return function update ( camera ) { | |
var needsUpdate = focalLength !== camera.focalLength || fov !== camera.fov || | |
aspect !== camera.aspect * this.aspect || near !== camera.near || | |
far !== camera.far; | |
if ( needsUpdate ) { | |
focalLength = camera.focalLength; | |
fov = camera.fov; | |
aspect = camera.aspect * this.aspect; | |
near = camera.near; | |
far = camera.far; | |
// Off-axis stereoscopic effect based on | |
// http://paulbourke.net/stereographics/stereorender/ | |
var projectionMatrix = camera.projectionMatrix.clone(); | |
var eyeSep = 0.064 / 2; | |
var eyeSepOnProjection = eyeSep * near / focalLength; | |
var ymax = near * Math.tan( THREE.Math.degToRad( fov * 0.5 ) ); | |
var xmin, xmax; | |
// translate xOffset | |
eyeLeft.elements[ 12 ] = - eyeSep; | |
eyeRight.elements[ 12 ] = eyeSep; | |
// for left eye | |
xmin = - ymax * aspect + eyeSepOnProjection; | |
xmax = ymax * aspect + eyeSepOnProjection; | |
projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); | |
projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); | |
this.cameraL.projectionMatrix.copy( projectionMatrix ); | |
// for right eye | |
xmin = - ymax * aspect - eyeSepOnProjection; | |
xmax = ymax * aspect - eyeSepOnProjection; | |
projectionMatrix.elements[ 0 ] = 2 * near / ( xmax - xmin ); | |
projectionMatrix.elements[ 8 ] = ( xmax + xmin ) / ( xmax - xmin ); | |
this.cameraR.projectionMatrix.copy( projectionMatrix ); | |
} | |
this.cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); | |
this.cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); | |
}; | |
} )() | |
}; | |
// File:src/lights/Light.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Light = function ( color, intensity ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Light'; | |
this.color = new THREE.Color( color ); | |
this.intensity = intensity !== undefined ? intensity : 1; | |
this.receiveShadow = undefined; | |
}; | |
THREE.Light.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Light.prototype.constructor = THREE.Light; | |
THREE.Light.prototype.copy = function ( source ) { | |
THREE.Object3D.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.intensity = source.intensity; | |
return this; | |
}; | |
THREE.Light.prototype.toJSON = function ( meta ) { | |
var data = THREE.Object3D.prototype.toJSON.call( this, meta ); | |
data.object.color = this.color.getHex(); | |
data.object.intensity = this.intensity; | |
if ( this.groundColor !== undefined ) data.object.groundColor = this.groundColor.getHex(); | |
if ( this.distance !== undefined ) data.object.distance = this.distance; | |
if ( this.angle !== undefined ) data.object.angle = this.angle; | |
if ( this.decay !== undefined ) data.object.decay = this.decay; | |
if ( this.penumbra !== undefined ) data.object.penumbra = this.penumbra; | |
return data; | |
}; | |
// File:src/lights/LightShadow.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.LightShadow = function ( camera ) { | |
this.camera = camera; | |
this.bias = 0; | |
this.radius = 1; | |
this.mapSize = new THREE.Vector2( 512, 512 ); | |
this.map = null; | |
this.matrix = new THREE.Matrix4(); | |
}; | |
THREE.LightShadow.prototype = { | |
constructor: THREE.LightShadow, | |
copy: function ( source ) { | |
this.camera = source.camera.clone(); | |
this.bias = source.bias; | |
this.radius = source.radius; | |
this.mapSize.copy( source.mapSize ); | |
return this; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
} | |
}; | |
// File:src/lights/AmbientLight.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.AmbientLight = function ( color, intensity ) { | |
THREE.Light.call( this, color, intensity ); | |
this.type = 'AmbientLight'; | |
this.castShadow = undefined; | |
}; | |
THREE.AmbientLight.prototype = Object.create( THREE.Light.prototype ); | |
THREE.AmbientLight.prototype.constructor = THREE.AmbientLight; | |
// File:src/lights/DirectionalLight.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.DirectionalLight = function ( color, intensity ) { | |
THREE.Light.call( this, color, intensity ); | |
this.type = 'DirectionalLight'; | |
this.position.set( 0, 1, 0 ); | |
this.updateMatrix(); | |
this.target = new THREE.Object3D(); | |
this.shadow = new THREE.LightShadow( new THREE.OrthographicCamera( - 5, 5, 5, - 5, 0.5, 500 ) ); | |
}; | |
THREE.DirectionalLight.prototype = Object.create( THREE.Light.prototype ); | |
THREE.DirectionalLight.prototype.constructor = THREE.DirectionalLight; | |
THREE.DirectionalLight.prototype.copy = function ( source ) { | |
THREE.Light.prototype.copy.call( this, source ); | |
this.target = source.target.clone(); | |
this.shadow = source.shadow.clone(); | |
return this; | |
}; | |
// File:src/lights/HemisphereLight.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.HemisphereLight = function ( skyColor, groundColor, intensity ) { | |
THREE.Light.call( this, skyColor, intensity ); | |
this.type = 'HemisphereLight'; | |
this.castShadow = undefined; | |
this.position.set( 0, 1, 0 ); | |
this.updateMatrix(); | |
this.groundColor = new THREE.Color( groundColor ); | |
}; | |
THREE.HemisphereLight.prototype = Object.create( THREE.Light.prototype ); | |
THREE.HemisphereLight.prototype.constructor = THREE.HemisphereLight; | |
THREE.HemisphereLight.prototype.copy = function ( source ) { | |
THREE.Light.prototype.copy.call( this, source ); | |
this.groundColor.copy( source.groundColor ); | |
return this; | |
}; | |
// File:src/lights/PointLight.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.PointLight = function ( color, intensity, distance, decay ) { | |
THREE.Light.call( this, color, intensity ); | |
this.type = 'PointLight'; | |
this.distance = ( distance !== undefined ) ? distance : 0; | |
this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. | |
this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 90, 1, 0.5, 500 ) ); | |
}; | |
THREE.PointLight.prototype = Object.create( THREE.Light.prototype ); | |
THREE.PointLight.prototype.constructor = THREE.PointLight; | |
THREE.PointLight.prototype.copy = function ( source ) { | |
THREE.Light.prototype.copy.call( this, source ); | |
this.distance = source.distance; | |
this.decay = source.decay; | |
this.shadow = source.shadow.clone(); | |
return this; | |
}; | |
// File:src/lights/SpotLight.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.SpotLight = function ( color, intensity, distance, angle, penumbra, decay ) { | |
THREE.Light.call( this, color, intensity ); | |
this.type = 'SpotLight'; | |
this.position.set( 0, 1, 0 ); | |
this.updateMatrix(); | |
this.target = new THREE.Object3D(); | |
this.distance = ( distance !== undefined ) ? distance : 0; | |
this.angle = ( angle !== undefined ) ? angle : Math.PI / 3; | |
this.penumbra = ( penumbra !== undefined ) ? penumbra : 0; | |
this.decay = ( decay !== undefined ) ? decay : 1; // for physically correct lights, should be 2. | |
this.shadow = new THREE.LightShadow( new THREE.PerspectiveCamera( 50, 1, 0.5, 500 ) ); | |
}; | |
THREE.SpotLight.prototype = Object.create( THREE.Light.prototype ); | |
THREE.SpotLight.prototype.constructor = THREE.SpotLight; | |
THREE.SpotLight.prototype.copy = function ( source ) { | |
THREE.Light.prototype.copy.call( this, source ); | |
this.distance = source.distance; | |
this.angle = source.angle; | |
this.penumbra = source.penumbra; | |
this.decay = source.decay; | |
this.target = source.target.clone(); | |
this.shadow = source.shadow.clone(); | |
return this; | |
}; | |
// File:src/loaders/Cache.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Cache = { | |
enabled: false, | |
files: {}, | |
add: function ( key, file ) { | |
if ( this.enabled === false ) return; | |
// console.log( 'THREE.Cache', 'Adding key:', key ); | |
this.files[ key ] = file; | |
}, | |
get: function ( key ) { | |
if ( this.enabled === false ) return; | |
// console.log( 'THREE.Cache', 'Checking key:', key ); | |
return this.files[ key ]; | |
}, | |
remove: function ( key ) { | |
delete this.files[ key ]; | |
}, | |
clear: function () { | |
this.files = {}; | |
} | |
}; | |
// File:src/loaders/Loader.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Loader = function () { | |
this.onLoadStart = function () {}; | |
this.onLoadProgress = function () {}; | |
this.onLoadComplete = function () {}; | |
}; | |
THREE.Loader.prototype = { | |
constructor: THREE.Loader, | |
crossOrigin: undefined, | |
extractUrlBase: function ( url ) { | |
var parts = url.split( '/' ); | |
if ( parts.length === 1 ) return './'; | |
parts.pop(); | |
return parts.join( '/' ) + '/'; | |
}, | |
initMaterials: function ( materials, texturePath, crossOrigin ) { | |
var array = []; | |
for ( var i = 0; i < materials.length; ++ i ) { | |
array[ i ] = this.createMaterial( materials[ i ], texturePath, crossOrigin ); | |
} | |
return array; | |
}, | |
createMaterial: ( function () { | |
var color, textureLoader, materialLoader; | |
return function ( m, texturePath, crossOrigin ) { | |
if ( color === undefined ) color = new THREE.Color(); | |
if ( textureLoader === undefined ) textureLoader = new THREE.TextureLoader(); | |
if ( materialLoader === undefined ) materialLoader = new THREE.MaterialLoader(); | |
// convert from old material format | |
var textures = {}; | |
function loadTexture( path, repeat, offset, wrap, anisotropy ) { | |
var fullPath = texturePath + path; | |
var loader = THREE.Loader.Handlers.get( fullPath ); | |
var texture; | |
if ( loader !== null ) { | |
texture = loader.load( fullPath ); | |
} else { | |
textureLoader.setCrossOrigin( crossOrigin ); | |
texture = textureLoader.load( fullPath ); | |
} | |
if ( repeat !== undefined ) { | |
texture.repeat.fromArray( repeat ); | |
if ( repeat[ 0 ] !== 1 ) texture.wrapS = THREE.RepeatWrapping; | |
if ( repeat[ 1 ] !== 1 ) texture.wrapT = THREE.RepeatWrapping; | |
} | |
if ( offset !== undefined ) { | |
texture.offset.fromArray( offset ); | |
} | |
if ( wrap !== undefined ) { | |
if ( wrap[ 0 ] === 'repeat' ) texture.wrapS = THREE.RepeatWrapping; | |
if ( wrap[ 0 ] === 'mirror' ) texture.wrapS = THREE.MirroredRepeatWrapping; | |
if ( wrap[ 1 ] === 'repeat' ) texture.wrapT = THREE.RepeatWrapping; | |
if ( wrap[ 1 ] === 'mirror' ) texture.wrapT = THREE.MirroredRepeatWrapping; | |
} | |
if ( anisotropy !== undefined ) { | |
texture.anisotropy = anisotropy; | |
} | |
var uuid = THREE.Math.generateUUID(); | |
textures[ uuid ] = texture; | |
return uuid; | |
} | |
// | |
var json = { | |
uuid: THREE.Math.generateUUID(), | |
type: 'MeshLambertMaterial' | |
}; | |
for ( var name in m ) { | |
var value = m[ name ]; | |
switch ( name ) { | |
case 'DbgColor': | |
case 'DbgIndex': | |
case 'opticalDensity': | |
case 'illumination': | |
break; | |
case 'DbgName': | |
json.name = value; | |
break; | |
case 'blending': | |
json.blending = THREE[ value ]; | |
break; | |
case 'colorAmbient': | |
case 'mapAmbient': | |
console.warn( 'THREE.Loader.createMaterial:', name, 'is no longer supported.' ); | |
break; | |
case 'colorDiffuse': | |
json.color = color.fromArray( value ).getHex(); | |
break; | |
case 'colorSpecular': | |
json.specular = color.fromArray( value ).getHex(); | |
break; | |
case 'colorEmissive': | |
json.emissive = color.fromArray( value ).getHex(); | |
break; | |
case 'specularCoef': | |
json.shininess = value; | |
break; | |
case 'shading': | |
if ( value.toLowerCase() === 'basic' ) json.type = 'MeshBasicMaterial'; | |
if ( value.toLowerCase() === 'phong' ) json.type = 'MeshPhongMaterial'; | |
break; | |
case 'mapDiffuse': | |
json.map = loadTexture( value, m.mapDiffuseRepeat, m.mapDiffuseOffset, m.mapDiffuseWrap, m.mapDiffuseAnisotropy ); | |
break; | |
case 'mapDiffuseRepeat': | |
case 'mapDiffuseOffset': | |
case 'mapDiffuseWrap': | |
case 'mapDiffuseAnisotropy': | |
break; | |
case 'mapLight': | |
json.lightMap = loadTexture( value, m.mapLightRepeat, m.mapLightOffset, m.mapLightWrap, m.mapLightAnisotropy ); | |
break; | |
case 'mapLightRepeat': | |
case 'mapLightOffset': | |
case 'mapLightWrap': | |
case 'mapLightAnisotropy': | |
break; | |
case 'mapAO': | |
json.aoMap = loadTexture( value, m.mapAORepeat, m.mapAOOffset, m.mapAOWrap, m.mapAOAnisotropy ); | |
break; | |
case 'mapAORepeat': | |
case 'mapAOOffset': | |
case 'mapAOWrap': | |
case 'mapAOAnisotropy': | |
break; | |
case 'mapBump': | |
json.bumpMap = loadTexture( value, m.mapBumpRepeat, m.mapBumpOffset, m.mapBumpWrap, m.mapBumpAnisotropy ); | |
break; | |
case 'mapBumpScale': | |
json.bumpScale = value; | |
break; | |
case 'mapBumpRepeat': | |
case 'mapBumpOffset': | |
case 'mapBumpWrap': | |
case 'mapBumpAnisotropy': | |
break; | |
case 'mapNormal': | |
json.normalMap = loadTexture( value, m.mapNormalRepeat, m.mapNormalOffset, m.mapNormalWrap, m.mapNormalAnisotropy ); | |
break; | |
case 'mapNormalFactor': | |
json.normalScale = [ value, value ]; | |
break; | |
case 'mapNormalRepeat': | |
case 'mapNormalOffset': | |
case 'mapNormalWrap': | |
case 'mapNormalAnisotropy': | |
break; | |
case 'mapSpecular': | |
json.specularMap = loadTexture( value, m.mapSpecularRepeat, m.mapSpecularOffset, m.mapSpecularWrap, m.mapSpecularAnisotropy ); | |
break; | |
case 'mapSpecularRepeat': | |
case 'mapSpecularOffset': | |
case 'mapSpecularWrap': | |
case 'mapSpecularAnisotropy': | |
break; | |
case 'mapAlpha': | |
json.alphaMap = loadTexture( value, m.mapAlphaRepeat, m.mapAlphaOffset, m.mapAlphaWrap, m.mapAlphaAnisotropy ); | |
break; | |
case 'mapAlphaRepeat': | |
case 'mapAlphaOffset': | |
case 'mapAlphaWrap': | |
case 'mapAlphaAnisotropy': | |
break; | |
case 'flipSided': | |
json.side = THREE.BackSide; | |
break; | |
case 'doubleSided': | |
json.side = THREE.DoubleSide; | |
break; | |
case 'transparency': | |
console.warn( 'THREE.Loader.createMaterial: transparency has been renamed to opacity' ); | |
json.opacity = value; | |
break; | |
case 'depthTest': | |
case 'depthWrite': | |
case 'colorWrite': | |
case 'opacity': | |
case 'reflectivity': | |
case 'transparent': | |
case 'visible': | |
case 'wireframe': | |
json[ name ] = value; | |
break; | |
case 'vertexColors': | |
if ( value === true ) json.vertexColors = THREE.VertexColors; | |
if ( value === 'face' ) json.vertexColors = THREE.FaceColors; | |
break; | |
default: | |
console.error( 'THREE.Loader.createMaterial: Unsupported', name, value ); | |
break; | |
} | |
} | |
if ( json.type === 'MeshBasicMaterial' ) delete json.emissive; | |
if ( json.type !== 'MeshPhongMaterial' ) delete json.specular; | |
if ( json.opacity < 1 ) json.transparent = true; | |
materialLoader.setTextures( textures ); | |
return materialLoader.parse( json ); | |
}; | |
} )() | |
}; | |
THREE.Loader.Handlers = { | |
handlers: [], | |
add: function ( regex, loader ) { | |
this.handlers.push( regex, loader ); | |
}, | |
get: function ( file ) { | |
var handlers = this.handlers; | |
for ( var i = 0, l = handlers.length; i < l; i += 2 ) { | |
var regex = handlers[ i ]; | |
var loader = handlers[ i + 1 ]; | |
if ( regex.test( file ) ) { | |
return loader; | |
} | |
} | |
return null; | |
} | |
}; | |
// File:src/loaders/XHRLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.XHRLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.XHRLoader.prototype = { | |
constructor: THREE.XHRLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
if ( this.path !== undefined ) url = this.path + url; | |
var scope = this; | |
var cached = THREE.Cache.get( url ); | |
if ( cached !== undefined ) { | |
if ( onLoad ) { | |
setTimeout( function () { | |
onLoad( cached ); | |
}, 0 ); | |
} | |
return cached; | |
} | |
var request = new XMLHttpRequest(); | |
request.overrideMimeType( 'text/plain' ); | |
request.open( 'GET', url, true ); | |
request.addEventListener( 'load', function ( event ) { | |
var response = event.target.response; | |
THREE.Cache.add( url, response ); | |
if ( this.status === 200 ) { | |
if ( onLoad ) onLoad( response ); | |
scope.manager.itemEnd( url ); | |
} else if ( this.status === 0 ) { | |
// Some browsers return HTTP Status 0 when using non-http protocol | |
// e.g. 'file://' or 'data://'. Handle as success. | |
console.warn( 'THREE.XHRLoader: HTTP Status 0 received.' ); | |
if ( onLoad ) onLoad( response ); | |
scope.manager.itemEnd( url ); | |
} else { | |
if ( onError ) onError( event ); | |
scope.manager.itemError( url ); | |
} | |
}, false ); | |
if ( onProgress !== undefined ) { | |
request.addEventListener( 'progress', function ( event ) { | |
onProgress( event ); | |
}, false ); | |
} | |
request.addEventListener( 'error', function ( event ) { | |
if ( onError ) onError( event ); | |
scope.manager.itemError( url ); | |
}, false ); | |
if ( this.responseType !== undefined ) request.responseType = this.responseType; | |
if ( this.withCredentials !== undefined ) request.withCredentials = this.withCredentials; | |
request.send( null ); | |
scope.manager.itemStart( url ); | |
return request; | |
}, | |
setPath: function ( value ) { | |
this.path = value; | |
}, | |
setResponseType: function ( value ) { | |
this.responseType = value; | |
}, | |
setWithCredentials: function ( value ) { | |
this.withCredentials = value; | |
} | |
}; | |
// File:src/loaders/FontLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.FontLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.FontLoader.prototype = { | |
constructor: THREE.FontLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var loader = new THREE.XHRLoader( this.manager ); | |
loader.load( url, function ( text ) { | |
onLoad( new THREE.Font( JSON.parse( text.substring( 65, text.length - 2 ) ) ) ); | |
}, onProgress, onError ); | |
} | |
}; | |
// File:src/loaders/ImageLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.ImageLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.ImageLoader.prototype = { | |
constructor: THREE.ImageLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
if ( this.path !== undefined ) url = this.path + url; | |
var scope = this; | |
var cached = THREE.Cache.get( url ); | |
if ( cached !== undefined ) { | |
scope.manager.itemStart( url ); | |
if ( onLoad ) { | |
setTimeout( function () { | |
onLoad( cached ); | |
scope.manager.itemEnd( url ); | |
}, 0 ); | |
} else { | |
scope.manager.itemEnd( url ); | |
} | |
return cached; | |
} | |
var image = document.createElement( 'img' ); | |
image.addEventListener( 'load', function ( event ) { | |
THREE.Cache.add( url, this ); | |
if ( onLoad ) onLoad( this ); | |
scope.manager.itemEnd( url ); | |
}, false ); | |
if ( onProgress !== undefined ) { | |
image.addEventListener( 'progress', function ( event ) { | |
onProgress( event ); | |
}, false ); | |
} | |
image.addEventListener( 'error', function ( event ) { | |
if ( onError ) onError( event ); | |
scope.manager.itemError( url ); | |
}, false ); | |
if ( this.crossOrigin !== undefined ) image.crossOrigin = this.crossOrigin; | |
scope.manager.itemStart( url ); | |
image.src = url; | |
return image; | |
}, | |
setCrossOrigin: function ( value ) { | |
this.crossOrigin = value; | |
}, | |
setPath: function ( value ) { | |
this.path = value; | |
} | |
}; | |
// File:src/loaders/JSONLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.JSONLoader = function ( manager ) { | |
if ( typeof manager === 'boolean' ) { | |
console.warn( 'THREE.JSONLoader: showStatus parameter has been removed from constructor.' ); | |
manager = undefined; | |
} | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
this.withCredentials = false; | |
}; | |
THREE.JSONLoader.prototype = { | |
constructor: THREE.JSONLoader, | |
// Deprecated | |
get statusDomElement () { | |
if ( this._statusDomElement === undefined ) { | |
this._statusDomElement = document.createElement( 'div' ); | |
} | |
console.warn( 'THREE.JSONLoader: .statusDomElement has been removed.' ); | |
return this._statusDomElement; | |
}, | |
load: function( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var texturePath = this.texturePath && ( typeof this.texturePath === "string" ) ? this.texturePath : THREE.Loader.prototype.extractUrlBase( url ); | |
var loader = new THREE.XHRLoader( this.manager ); | |
loader.setWithCredentials( this.withCredentials ); | |
loader.load( url, function ( text ) { | |
var json = JSON.parse( text ); | |
var metadata = json.metadata; | |
if ( metadata !== undefined ) { | |
var type = metadata.type; | |
if ( type !== undefined ) { | |
if ( type.toLowerCase() === 'object' ) { | |
console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.ObjectLoader instead.' ); | |
return; | |
} | |
if ( type.toLowerCase() === 'scene' ) { | |
console.error( 'THREE.JSONLoader: ' + url + ' should be loaded with THREE.SceneLoader instead.' ); | |
return; | |
} | |
} | |
} | |
var object = scope.parse( json, texturePath ); | |
onLoad( object.geometry, object.materials ); | |
}, onProgress, onError ); | |
}, | |
setTexturePath: function ( value ) { | |
this.texturePath = value; | |
}, | |
parse: function ( json, texturePath ) { | |
var geometry = new THREE.Geometry(), | |
scale = ( json.scale !== undefined ) ? 1.0 / json.scale : 1.0; | |
parseModel( scale ); | |
parseSkin(); | |
parseMorphing( scale ); | |
parseAnimations(); | |
geometry.computeFaceNormals(); | |
geometry.computeBoundingSphere(); | |
function parseModel( scale ) { | |
function isBitSet( value, position ) { | |
return value & ( 1 << position ); | |
} | |
var i, j, fi, | |
offset, zLength, | |
colorIndex, normalIndex, uvIndex, materialIndex, | |
type, | |
isQuad, | |
hasMaterial, | |
hasFaceVertexUv, | |
hasFaceNormal, hasFaceVertexNormal, | |
hasFaceColor, hasFaceVertexColor, | |
vertex, face, faceA, faceB, hex, normal, | |
uvLayer, uv, u, v, | |
faces = json.faces, | |
vertices = json.vertices, | |
normals = json.normals, | |
colors = json.colors, | |
nUvLayers = 0; | |
if ( json.uvs !== undefined ) { | |
// disregard empty arrays | |
for ( i = 0; i < json.uvs.length; i ++ ) { | |
if ( json.uvs[ i ].length ) nUvLayers ++; | |
} | |
for ( i = 0; i < nUvLayers; i ++ ) { | |
geometry.faceVertexUvs[ i ] = []; | |
} | |
} | |
offset = 0; | |
zLength = vertices.length; | |
while ( offset < zLength ) { | |
vertex = new THREE.Vector3(); | |
vertex.x = vertices[ offset ++ ] * scale; | |
vertex.y = vertices[ offset ++ ] * scale; | |
vertex.z = vertices[ offset ++ ] * scale; | |
geometry.vertices.push( vertex ); | |
} | |
offset = 0; | |
zLength = faces.length; | |
while ( offset < zLength ) { | |
type = faces[ offset ++ ]; | |
isQuad = isBitSet( type, 0 ); | |
hasMaterial = isBitSet( type, 1 ); | |
hasFaceVertexUv = isBitSet( type, 3 ); | |
hasFaceNormal = isBitSet( type, 4 ); | |
hasFaceVertexNormal = isBitSet( type, 5 ); | |
hasFaceColor = isBitSet( type, 6 ); | |
hasFaceVertexColor = isBitSet( type, 7 ); | |
// console.log("type", type, "bits", isQuad, hasMaterial, hasFaceVertexUv, hasFaceNormal, hasFaceVertexNormal, hasFaceColor, hasFaceVertexColor); | |
if ( isQuad ) { | |
faceA = new THREE.Face3(); | |
faceA.a = faces[ offset ]; | |
faceA.b = faces[ offset + 1 ]; | |
faceA.c = faces[ offset + 3 ]; | |
faceB = new THREE.Face3(); | |
faceB.a = faces[ offset + 1 ]; | |
faceB.b = faces[ offset + 2 ]; | |
faceB.c = faces[ offset + 3 ]; | |
offset += 4; | |
if ( hasMaterial ) { | |
materialIndex = faces[ offset ++ ]; | |
faceA.materialIndex = materialIndex; | |
faceB.materialIndex = materialIndex; | |
} | |
// to get face <=> uv index correspondence | |
fi = geometry.faces.length; | |
if ( hasFaceVertexUv ) { | |
for ( i = 0; i < nUvLayers; i ++ ) { | |
uvLayer = json.uvs[ i ]; | |
geometry.faceVertexUvs[ i ][ fi ] = []; | |
geometry.faceVertexUvs[ i ][ fi + 1 ] = []; | |
for ( j = 0; j < 4; j ++ ) { | |
uvIndex = faces[ offset ++ ]; | |
u = uvLayer[ uvIndex * 2 ]; | |
v = uvLayer[ uvIndex * 2 + 1 ]; | |
uv = new THREE.Vector2( u, v ); | |
if ( j !== 2 ) geometry.faceVertexUvs[ i ][ fi ].push( uv ); | |
if ( j !== 0 ) geometry.faceVertexUvs[ i ][ fi + 1 ].push( uv ); | |
} | |
} | |
} | |
if ( hasFaceNormal ) { | |
normalIndex = faces[ offset ++ ] * 3; | |
faceA.normal.set( | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ] | |
); | |
faceB.normal.copy( faceA.normal ); | |
} | |
if ( hasFaceVertexNormal ) { | |
for ( i = 0; i < 4; i ++ ) { | |
normalIndex = faces[ offset ++ ] * 3; | |
normal = new THREE.Vector3( | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ] | |
); | |
if ( i !== 2 ) faceA.vertexNormals.push( normal ); | |
if ( i !== 0 ) faceB.vertexNormals.push( normal ); | |
} | |
} | |
if ( hasFaceColor ) { | |
colorIndex = faces[ offset ++ ]; | |
hex = colors[ colorIndex ]; | |
faceA.color.setHex( hex ); | |
faceB.color.setHex( hex ); | |
} | |
if ( hasFaceVertexColor ) { | |
for ( i = 0; i < 4; i ++ ) { | |
colorIndex = faces[ offset ++ ]; | |
hex = colors[ colorIndex ]; | |
if ( i !== 2 ) faceA.vertexColors.push( new THREE.Color( hex ) ); | |
if ( i !== 0 ) faceB.vertexColors.push( new THREE.Color( hex ) ); | |
} | |
} | |
geometry.faces.push( faceA ); | |
geometry.faces.push( faceB ); | |
} else { | |
face = new THREE.Face3(); | |
face.a = faces[ offset ++ ]; | |
face.b = faces[ offset ++ ]; | |
face.c = faces[ offset ++ ]; | |
if ( hasMaterial ) { | |
materialIndex = faces[ offset ++ ]; | |
face.materialIndex = materialIndex; | |
} | |
// to get face <=> uv index correspondence | |
fi = geometry.faces.length; | |
if ( hasFaceVertexUv ) { | |
for ( i = 0; i < nUvLayers; i ++ ) { | |
uvLayer = json.uvs[ i ]; | |
geometry.faceVertexUvs[ i ][ fi ] = []; | |
for ( j = 0; j < 3; j ++ ) { | |
uvIndex = faces[ offset ++ ]; | |
u = uvLayer[ uvIndex * 2 ]; | |
v = uvLayer[ uvIndex * 2 + 1 ]; | |
uv = new THREE.Vector2( u, v ); | |
geometry.faceVertexUvs[ i ][ fi ].push( uv ); | |
} | |
} | |
} | |
if ( hasFaceNormal ) { | |
normalIndex = faces[ offset ++ ] * 3; | |
face.normal.set( | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ] | |
); | |
} | |
if ( hasFaceVertexNormal ) { | |
for ( i = 0; i < 3; i ++ ) { | |
normalIndex = faces[ offset ++ ] * 3; | |
normal = new THREE.Vector3( | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ++ ], | |
normals[ normalIndex ] | |
); | |
face.vertexNormals.push( normal ); | |
} | |
} | |
if ( hasFaceColor ) { | |
colorIndex = faces[ offset ++ ]; | |
face.color.setHex( colors[ colorIndex ] ); | |
} | |
if ( hasFaceVertexColor ) { | |
for ( i = 0; i < 3; i ++ ) { | |
colorIndex = faces[ offset ++ ]; | |
face.vertexColors.push( new THREE.Color( colors[ colorIndex ] ) ); | |
} | |
} | |
geometry.faces.push( face ); | |
} | |
} | |
}; | |
function parseSkin() { | |
var influencesPerVertex = ( json.influencesPerVertex !== undefined ) ? json.influencesPerVertex : 2; | |
if ( json.skinWeights ) { | |
for ( var i = 0, l = json.skinWeights.length; i < l; i += influencesPerVertex ) { | |
var x = json.skinWeights[ i ]; | |
var y = ( influencesPerVertex > 1 ) ? json.skinWeights[ i + 1 ] : 0; | |
var z = ( influencesPerVertex > 2 ) ? json.skinWeights[ i + 2 ] : 0; | |
var w = ( influencesPerVertex > 3 ) ? json.skinWeights[ i + 3 ] : 0; | |
geometry.skinWeights.push( new THREE.Vector4( x, y, z, w ) ); | |
} | |
} | |
if ( json.skinIndices ) { | |
for ( var i = 0, l = json.skinIndices.length; i < l; i += influencesPerVertex ) { | |
var a = json.skinIndices[ i ]; | |
var b = ( influencesPerVertex > 1 ) ? json.skinIndices[ i + 1 ] : 0; | |
var c = ( influencesPerVertex > 2 ) ? json.skinIndices[ i + 2 ] : 0; | |
var d = ( influencesPerVertex > 3 ) ? json.skinIndices[ i + 3 ] : 0; | |
geometry.skinIndices.push( new THREE.Vector4( a, b, c, d ) ); | |
} | |
} | |
geometry.bones = json.bones; | |
if ( geometry.bones && geometry.bones.length > 0 && ( geometry.skinWeights.length !== geometry.skinIndices.length || geometry.skinIndices.length !== geometry.vertices.length ) ) { | |
console.warn( 'When skinning, number of vertices (' + geometry.vertices.length + '), skinIndices (' + | |
geometry.skinIndices.length + '), and skinWeights (' + geometry.skinWeights.length + ') should match.' ); | |
} | |
}; | |
function parseMorphing( scale ) { | |
if ( json.morphTargets !== undefined ) { | |
for ( var i = 0, l = json.morphTargets.length; i < l; i ++ ) { | |
geometry.morphTargets[ i ] = {}; | |
geometry.morphTargets[ i ].name = json.morphTargets[ i ].name; | |
geometry.morphTargets[ i ].vertices = []; | |
var dstVertices = geometry.morphTargets[ i ].vertices; | |
var srcVertices = json.morphTargets[ i ].vertices; | |
for ( var v = 0, vl = srcVertices.length; v < vl; v += 3 ) { | |
var vertex = new THREE.Vector3(); | |
vertex.x = srcVertices[ v ] * scale; | |
vertex.y = srcVertices[ v + 1 ] * scale; | |
vertex.z = srcVertices[ v + 2 ] * scale; | |
dstVertices.push( vertex ); | |
} | |
} | |
} | |
if ( json.morphColors !== undefined && json.morphColors.length > 0 ) { | |
console.warn( 'THREE.JSONLoader: "morphColors" no longer supported. Using them as face colors.' ); | |
var faces = geometry.faces; | |
var morphColors = json.morphColors[ 0 ].colors; | |
for ( var i = 0, l = faces.length; i < l; i ++ ) { | |
faces[ i ].color.fromArray( morphColors, i * 3 ); | |
} | |
} | |
} | |
function parseAnimations() { | |
var outputAnimations = []; | |
// parse old style Bone/Hierarchy animations | |
var animations = []; | |
if ( json.animation !== undefined ) { | |
animations.push( json.animation ); | |
} | |
if ( json.animations !== undefined ) { | |
if ( json.animations.length ) { | |
animations = animations.concat( json.animations ); | |
} else { | |
animations.push( json.animations ); | |
} | |
} | |
for ( var i = 0; i < animations.length; i ++ ) { | |
var clip = THREE.AnimationClip.parseAnimation( animations[ i ], geometry.bones ); | |
if ( clip ) outputAnimations.push( clip ); | |
} | |
// parse implicit morph animations | |
if ( geometry.morphTargets ) { | |
// TODO: Figure out what an appropraite FPS is for morph target animations -- defaulting to 10, but really it is completely arbitrary. | |
var morphAnimationClips = THREE.AnimationClip.CreateClipsFromMorphTargetSequences( geometry.morphTargets, 10 ); | |
outputAnimations = outputAnimations.concat( morphAnimationClips ); | |
} | |
if ( outputAnimations.length > 0 ) geometry.animations = outputAnimations; | |
}; | |
if ( json.materials === undefined || json.materials.length === 0 ) { | |
return { geometry: geometry }; | |
} else { | |
var materials = THREE.Loader.prototype.initMaterials( json.materials, texturePath, this.crossOrigin ); | |
return { geometry: geometry, materials: materials }; | |
} | |
} | |
}; | |
// File:src/loaders/LoadingManager.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.LoadingManager = function ( onLoad, onProgress, onError ) { | |
var scope = this; | |
var isLoading = false, itemsLoaded = 0, itemsTotal = 0; | |
this.onStart = undefined; | |
this.onLoad = onLoad; | |
this.onProgress = onProgress; | |
this.onError = onError; | |
this.itemStart = function ( url ) { | |
itemsTotal ++; | |
if ( isLoading === false ) { | |
if ( scope.onStart !== undefined ) { | |
scope.onStart( url, itemsLoaded, itemsTotal ); | |
} | |
} | |
isLoading = true; | |
}; | |
this.itemEnd = function ( url ) { | |
itemsLoaded ++; | |
if ( scope.onProgress !== undefined ) { | |
scope.onProgress( url, itemsLoaded, itemsTotal ); | |
} | |
if ( itemsLoaded === itemsTotal ) { | |
isLoading = false; | |
if ( scope.onLoad !== undefined ) { | |
scope.onLoad(); | |
} | |
} | |
}; | |
this.itemError = function ( url ) { | |
if ( scope.onError !== undefined ) { | |
scope.onError( url ); | |
} | |
}; | |
}; | |
THREE.DefaultLoadingManager = new THREE.LoadingManager(); | |
// File:src/loaders/BufferGeometryLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.BufferGeometryLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.BufferGeometryLoader.prototype = { | |
constructor: THREE.BufferGeometryLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var loader = new THREE.XHRLoader( scope.manager ); | |
loader.load( url, function ( text ) { | |
onLoad( scope.parse( JSON.parse( text ) ) ); | |
}, onProgress, onError ); | |
}, | |
parse: function ( json ) { | |
var geometry = new THREE.BufferGeometry(); | |
var index = json.data.index; | |
var TYPED_ARRAYS = { | |
'Int8Array': Int8Array, | |
'Uint8Array': Uint8Array, | |
'Uint8ClampedArray': Uint8ClampedArray, | |
'Int16Array': Int16Array, | |
'Uint16Array': Uint16Array, | |
'Int32Array': Int32Array, | |
'Uint32Array': Uint32Array, | |
'Float32Array': Float32Array, | |
'Float64Array': Float64Array | |
}; | |
if ( index !== undefined ) { | |
var typedArray = new TYPED_ARRAYS[ index.type ]( index.array ); | |
geometry.setIndex( new THREE.BufferAttribute( typedArray, 1 ) ); | |
} | |
var attributes = json.data.attributes; | |
for ( var key in attributes ) { | |
var attribute = attributes[ key ]; | |
var typedArray = new TYPED_ARRAYS[ attribute.type ]( attribute.array ); | |
geometry.addAttribute( key, new THREE.BufferAttribute( typedArray, attribute.itemSize ) ); | |
} | |
var groups = json.data.groups || json.data.drawcalls || json.data.offsets; | |
if ( groups !== undefined ) { | |
for ( var i = 0, n = groups.length; i !== n; ++ i ) { | |
var group = groups[ i ]; | |
geometry.addGroup( group.start, group.count, group.materialIndex ); | |
} | |
} | |
var boundingSphere = json.data.boundingSphere; | |
if ( boundingSphere !== undefined ) { | |
var center = new THREE.Vector3(); | |
if ( boundingSphere.center !== undefined ) { | |
center.fromArray( boundingSphere.center ); | |
} | |
geometry.boundingSphere = new THREE.Sphere( center, boundingSphere.radius ); | |
} | |
return geometry; | |
} | |
}; | |
// File:src/loaders/MaterialLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.MaterialLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
this.textures = {}; | |
}; | |
THREE.MaterialLoader.prototype = { | |
constructor: THREE.MaterialLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var loader = new THREE.XHRLoader( scope.manager ); | |
loader.load( url, function ( text ) { | |
onLoad( scope.parse( JSON.parse( text ) ) ); | |
}, onProgress, onError ); | |
}, | |
setTextures: function ( value ) { | |
this.textures = value; | |
}, | |
getTexture: function ( name ) { | |
var textures = this.textures; | |
if ( textures[ name ] === undefined ) { | |
console.warn( 'THREE.MaterialLoader: Undefined texture', name ); | |
} | |
return textures[ name ]; | |
}, | |
parse: function ( json ) { | |
var material = new THREE[ json.type ]; | |
if ( json.uuid !== undefined ) material.uuid = json.uuid; | |
if ( json.name !== undefined ) material.name = json.name; | |
if ( json.color !== undefined ) material.color.setHex( json.color ); | |
if ( json.roughness !== undefined ) material.roughness = json.roughness; | |
if ( json.metalness !== undefined ) material.metalness = json.metalness; | |
if ( json.emissive !== undefined ) material.emissive.setHex( json.emissive ); | |
if ( json.specular !== undefined ) material.specular.setHex( json.specular ); | |
if ( json.shininess !== undefined ) material.shininess = json.shininess; | |
if ( json.uniforms !== undefined ) material.uniforms = json.uniforms; | |
if ( json.vertexShader !== undefined ) material.vertexShader = json.vertexShader; | |
if ( json.fragmentShader !== undefined ) material.fragmentShader = json.fragmentShader; | |
if ( json.vertexColors !== undefined ) material.vertexColors = json.vertexColors; | |
if ( json.shading !== undefined ) material.shading = json.shading; | |
if ( json.blending !== undefined ) material.blending = json.blending; | |
if ( json.side !== undefined ) material.side = json.side; | |
if ( json.opacity !== undefined ) material.opacity = json.opacity; | |
if ( json.transparent !== undefined ) material.transparent = json.transparent; | |
if ( json.alphaTest !== undefined ) material.alphaTest = json.alphaTest; | |
if ( json.depthTest !== undefined ) material.depthTest = json.depthTest; | |
if ( json.depthWrite !== undefined ) material.depthWrite = json.depthWrite; | |
if ( json.colorWrite !== undefined ) material.colorWrite = json.colorWrite; | |
if ( json.wireframe !== undefined ) material.wireframe = json.wireframe; | |
if ( json.wireframeLinewidth !== undefined ) material.wireframeLinewidth = json.wireframeLinewidth; | |
// for PointsMaterial | |
if ( json.size !== undefined ) material.size = json.size; | |
if ( json.sizeAttenuation !== undefined ) material.sizeAttenuation = json.sizeAttenuation; | |
// maps | |
if ( json.map !== undefined ) material.map = this.getTexture( json.map ); | |
if ( json.alphaMap !== undefined ) { | |
material.alphaMap = this.getTexture( json.alphaMap ); | |
material.transparent = true; | |
} | |
if ( json.bumpMap !== undefined ) material.bumpMap = this.getTexture( json.bumpMap ); | |
if ( json.bumpScale !== undefined ) material.bumpScale = json.bumpScale; | |
if ( json.normalMap !== undefined ) material.normalMap = this.getTexture( json.normalMap ); | |
if ( json.normalScale !== undefined ) { | |
var normalScale = json.normalScale; | |
if ( Array.isArray( normalScale ) === false ) { | |
// Blender exporter used to export a scalar. See #7459 | |
normalScale = [ normalScale, normalScale ]; | |
} | |
material.normalScale = new THREE.Vector2().fromArray( normalScale ); | |
} | |
if ( json.displacementMap !== undefined ) material.displacementMap = this.getTexture( json.displacementMap ); | |
if ( json.displacementScale !== undefined ) material.displacementScale = json.displacementScale; | |
if ( json.displacementBias !== undefined ) material.displacementBias = json.displacementBias; | |
if ( json.roughnessMap !== undefined ) material.roughnessMap = this.getTexture( json.roughnessMap ); | |
if ( json.metalnessMap !== undefined ) material.metalnessMap = this.getTexture( json.metalnessMap ); | |
if ( json.emissiveMap !== undefined ) material.emissiveMap = this.getTexture( json.emissiveMap ); | |
if ( json.emissiveIntensity !== undefined ) material.emissiveIntensity = json.emissiveIntensity; | |
if ( json.specularMap !== undefined ) material.specularMap = this.getTexture( json.specularMap ); | |
if ( json.envMap !== undefined ) { | |
material.envMap = this.getTexture( json.envMap ); | |
material.combine = THREE.MultiplyOperation; | |
} | |
if ( json.reflectivity ) material.reflectivity = json.reflectivity; | |
if ( json.lightMap !== undefined ) material.lightMap = this.getTexture( json.lightMap ); | |
if ( json.lightMapIntensity !== undefined ) material.lightMapIntensity = json.lightMapIntensity; | |
if ( json.aoMap !== undefined ) material.aoMap = this.getTexture( json.aoMap ); | |
if ( json.aoMapIntensity !== undefined ) material.aoMapIntensity = json.aoMapIntensity; | |
// MultiMaterial | |
if ( json.materials !== undefined ) { | |
for ( var i = 0, l = json.materials.length; i < l; i ++ ) { | |
material.materials.push( this.parse( json.materials[ i ] ) ); | |
} | |
} | |
return material; | |
} | |
}; | |
// File:src/loaders/ObjectLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.ObjectLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
this.texturePath = ''; | |
}; | |
THREE.ObjectLoader.prototype = { | |
constructor: THREE.ObjectLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
if ( this.texturePath === '' ) { | |
this.texturePath = url.substring( 0, url.lastIndexOf( '/' ) + 1 ); | |
} | |
var scope = this; | |
var loader = new THREE.XHRLoader( scope.manager ); | |
loader.load( url, function ( text ) { | |
scope.parse( JSON.parse( text ), onLoad ); | |
}, onProgress, onError ); | |
}, | |
setTexturePath: function ( value ) { | |
this.texturePath = value; | |
}, | |
setCrossOrigin: function ( value ) { | |
this.crossOrigin = value; | |
}, | |
parse: function ( json, onLoad ) { | |
var geometries = this.parseGeometries( json.geometries ); | |
var images = this.parseImages( json.images, function () { | |
if ( onLoad !== undefined ) onLoad( object ); | |
} ); | |
var textures = this.parseTextures( json.textures, images ); | |
var materials = this.parseMaterials( json.materials, textures ); | |
var object = this.parseObject( json.object, geometries, materials ); | |
if ( json.animations ) { | |
object.animations = this.parseAnimations( json.animations ); | |
} | |
if ( json.images === undefined || json.images.length === 0 ) { | |
if ( onLoad !== undefined ) onLoad( object ); | |
} | |
return object; | |
}, | |
parseGeometries: function ( json ) { | |
var geometries = {}; | |
if ( json !== undefined ) { | |
var geometryLoader = new THREE.JSONLoader(); | |
var bufferGeometryLoader = new THREE.BufferGeometryLoader(); | |
for ( var i = 0, l = json.length; i < l; i ++ ) { | |
var geometry; | |
var data = json[ i ]; | |
switch ( data.type ) { | |
case 'PlaneGeometry': | |
case 'PlaneBufferGeometry': | |
geometry = new THREE[ data.type ]( | |
data.width, | |
data.height, | |
data.widthSegments, | |
data.heightSegments | |
); | |
break; | |
case 'BoxGeometry': | |
case 'CubeGeometry': // backwards compatible | |
geometry = new THREE.BoxGeometry( | |
data.width, | |
data.height, | |
data.depth, | |
data.widthSegments, | |
data.heightSegments, | |
data.depthSegments | |
); | |
break; | |
case 'CircleBufferGeometry': | |
geometry = new THREE.CircleBufferGeometry( | |
data.radius, | |
data.segments, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'CircleGeometry': | |
geometry = new THREE.CircleGeometry( | |
data.radius, | |
data.segments, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'CylinderGeometry': | |
geometry = new THREE.CylinderGeometry( | |
data.radiusTop, | |
data.radiusBottom, | |
data.height, | |
data.radialSegments, | |
data.heightSegments, | |
data.openEnded, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'SphereGeometry': | |
geometry = new THREE.SphereGeometry( | |
data.radius, | |
data.widthSegments, | |
data.heightSegments, | |
data.phiStart, | |
data.phiLength, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'SphereBufferGeometry': | |
geometry = new THREE.SphereBufferGeometry( | |
data.radius, | |
data.widthSegments, | |
data.heightSegments, | |
data.phiStart, | |
data.phiLength, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'DodecahedronGeometry': | |
geometry = new THREE.DodecahedronGeometry( | |
data.radius, | |
data.detail | |
); | |
break; | |
case 'IcosahedronGeometry': | |
geometry = new THREE.IcosahedronGeometry( | |
data.radius, | |
data.detail | |
); | |
break; | |
case 'OctahedronGeometry': | |
geometry = new THREE.OctahedronGeometry( | |
data.radius, | |
data.detail | |
); | |
break; | |
case 'TetrahedronGeometry': | |
geometry = new THREE.TetrahedronGeometry( | |
data.radius, | |
data.detail | |
); | |
break; | |
case 'RingGeometry': | |
geometry = new THREE.RingGeometry( | |
data.innerRadius, | |
data.outerRadius, | |
data.thetaSegments, | |
data.phiSegments, | |
data.thetaStart, | |
data.thetaLength | |
); | |
break; | |
case 'TorusGeometry': | |
geometry = new THREE.TorusGeometry( | |
data.radius, | |
data.tube, | |
data.radialSegments, | |
data.tubularSegments, | |
data.arc | |
); | |
break; | |
case 'TorusKnotGeometry': | |
geometry = new THREE.TorusKnotGeometry( | |
data.radius, | |
data.tube, | |
data.radialSegments, | |
data.tubularSegments, | |
data.p, | |
data.q, | |
data.heightScale | |
); | |
break; | |
case 'LatheGeometry': | |
geometry = new THREE.LatheGeometry( | |
data.points, | |
data.segments, | |
data.phiStart, | |
data.phiLength | |
); | |
break; | |
case 'BufferGeometry': | |
geometry = bufferGeometryLoader.parse( data ); | |
break; | |
case 'Geometry': | |
geometry = geometryLoader.parse( data.data, this.texturePath ).geometry; | |
break; | |
default: | |
console.warn( 'THREE.ObjectLoader: Unsupported geometry type "' + data.type + '"' ); | |
continue; | |
} | |
geometry.uuid = data.uuid; | |
if ( data.name !== undefined ) geometry.name = data.name; | |
geometries[ data.uuid ] = geometry; | |
} | |
} | |
return geometries; | |
}, | |
parseMaterials: function ( json, textures ) { | |
var materials = {}; | |
if ( json !== undefined ) { | |
var loader = new THREE.MaterialLoader(); | |
loader.setTextures( textures ); | |
for ( var i = 0, l = json.length; i < l; i ++ ) { | |
var material = loader.parse( json[ i ] ); | |
materials[ material.uuid ] = material; | |
} | |
} | |
return materials; | |
}, | |
parseAnimations: function ( json ) { | |
var animations = []; | |
for ( var i = 0; i < json.length; i ++ ) { | |
var clip = THREE.AnimationClip.parse( json[ i ] ); | |
animations.push( clip ); | |
} | |
return animations; | |
}, | |
parseImages: function ( json, onLoad ) { | |
var scope = this; | |
var images = {}; | |
function loadImage( url ) { | |
scope.manager.itemStart( url ); | |
return loader.load( url, function () { | |
scope.manager.itemEnd( url ); | |
} ); | |
} | |
if ( json !== undefined && json.length > 0 ) { | |
var manager = new THREE.LoadingManager( onLoad ); | |
var loader = new THREE.ImageLoader( manager ); | |
loader.setCrossOrigin( this.crossOrigin ); | |
for ( var i = 0, l = json.length; i < l; i ++ ) { | |
var image = json[ i ]; | |
var path = /^(\/\/)|([a-z]+:(\/\/)?)/i.test( image.url ) ? image.url : scope.texturePath + image.url; | |
images[ image.uuid ] = loadImage( path ); | |
} | |
} | |
return images; | |
}, | |
parseTextures: function ( json, images ) { | |
function parseConstant( value ) { | |
if ( typeof( value ) === 'number' ) return value; | |
console.warn( 'THREE.ObjectLoader.parseTexture: Constant should be in numeric form.', value ); | |
return THREE[ value ]; | |
} | |
var textures = {}; | |
if ( json !== undefined ) { | |
for ( var i = 0, l = json.length; i < l; i ++ ) { | |
var data = json[ i ]; | |
if ( data.image === undefined ) { | |
console.warn( 'THREE.ObjectLoader: No "image" specified for', data.uuid ); | |
} | |
if ( images[ data.image ] === undefined ) { | |
console.warn( 'THREE.ObjectLoader: Undefined image', data.image ); | |
} | |
var texture = new THREE.Texture( images[ data.image ] ); | |
texture.needsUpdate = true; | |
texture.uuid = data.uuid; | |
if ( data.name !== undefined ) texture.name = data.name; | |
if ( data.mapping !== undefined ) texture.mapping = parseConstant( data.mapping ); | |
if ( data.offset !== undefined ) texture.offset = new THREE.Vector2( data.offset[ 0 ], data.offset[ 1 ] ); | |
if ( data.repeat !== undefined ) texture.repeat = new THREE.Vector2( data.repeat[ 0 ], data.repeat[ 1 ] ); | |
if ( data.minFilter !== undefined ) texture.minFilter = parseConstant( data.minFilter ); | |
if ( data.magFilter !== undefined ) texture.magFilter = parseConstant( data.magFilter ); | |
if ( data.anisotropy !== undefined ) texture.anisotropy = data.anisotropy; | |
if ( Array.isArray( data.wrap ) ) { | |
texture.wrapS = parseConstant( data.wrap[ 0 ] ); | |
texture.wrapT = parseConstant( data.wrap[ 1 ] ); | |
} | |
textures[ data.uuid ] = texture; | |
} | |
} | |
return textures; | |
}, | |
parseObject: function () { | |
var matrix = new THREE.Matrix4(); | |
return function ( data, geometries, materials ) { | |
var object; | |
function getGeometry( name ) { | |
if ( geometries[ name ] === undefined ) { | |
console.warn( 'THREE.ObjectLoader: Undefined geometry', name ); | |
} | |
return geometries[ name ]; | |
} | |
function getMaterial( name ) { | |
if ( name === undefined ) return undefined; | |
if ( materials[ name ] === undefined ) { | |
console.warn( 'THREE.ObjectLoader: Undefined material', name ); | |
} | |
return materials[ name ]; | |
} | |
switch ( data.type ) { | |
case 'Scene': | |
object = new THREE.Scene(); | |
break; | |
case 'PerspectiveCamera': | |
object = new THREE.PerspectiveCamera( data.fov, data.aspect, data.near, data.far ); | |
break; | |
case 'OrthographicCamera': | |
object = new THREE.OrthographicCamera( data.left, data.right, data.top, data.bottom, data.near, data.far ); | |
break; | |
case 'AmbientLight': | |
object = new THREE.AmbientLight( data.color, data.intensity ); | |
break; | |
case 'DirectionalLight': | |
object = new THREE.DirectionalLight( data.color, data.intensity ); | |
break; | |
case 'PointLight': | |
object = new THREE.PointLight( data.color, data.intensity, data.distance, data.decay ); | |
break; | |
case 'SpotLight': | |
object = new THREE.SpotLight( data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay ); | |
break; | |
case 'HemisphereLight': | |
object = new THREE.HemisphereLight( data.color, data.groundColor, data.intensity ); | |
break; | |
case 'Mesh': | |
var geometry = getGeometry( data.geometry ); | |
var material = getMaterial( data.material ); | |
if ( geometry.bones && geometry.bones.length > 0 ) { | |
object = new THREE.SkinnedMesh( geometry, material ); | |
} else { | |
object = new THREE.Mesh( geometry, material ); | |
} | |
break; | |
case 'LOD': | |
object = new THREE.LOD(); | |
break; | |
case 'Line': | |
object = new THREE.Line( getGeometry( data.geometry ), getMaterial( data.material ), data.mode ); | |
break; | |
case 'PointCloud': | |
case 'Points': | |
object = new THREE.Points( getGeometry( data.geometry ), getMaterial( data.material ) ); | |
break; | |
case 'Sprite': | |
object = new THREE.Sprite( getMaterial( data.material ) ); | |
break; | |
case 'Group': | |
object = new THREE.Group(); | |
break; | |
default: | |
object = new THREE.Object3D(); | |
} | |
object.uuid = data.uuid; | |
if ( data.name !== undefined ) object.name = data.name; | |
if ( data.matrix !== undefined ) { | |
matrix.fromArray( data.matrix ); | |
matrix.decompose( object.position, object.quaternion, object.scale ); | |
} else { | |
if ( data.position !== undefined ) object.position.fromArray( data.position ); | |
if ( data.rotation !== undefined ) object.rotation.fromArray( data.rotation ); | |
if ( data.scale !== undefined ) object.scale.fromArray( data.scale ); | |
} | |
if ( data.castShadow !== undefined ) object.castShadow = data.castShadow; | |
if ( data.receiveShadow !== undefined ) object.receiveShadow = data.receiveShadow; | |
if ( data.visible !== undefined ) object.visible = data.visible; | |
if ( data.userData !== undefined ) object.userData = data.userData; | |
if ( data.children !== undefined ) { | |
for ( var child in data.children ) { | |
object.add( this.parseObject( data.children[ child ], geometries, materials ) ); | |
} | |
} | |
if ( data.type === 'LOD' ) { | |
var levels = data.levels; | |
for ( var l = 0; l < levels.length; l ++ ) { | |
var level = levels[ l ]; | |
var child = object.getObjectByProperty( 'uuid', level.object ); | |
if ( child !== undefined ) { | |
object.addLevel( child, level.distance ); | |
} | |
} | |
} | |
return object; | |
} | |
}() | |
}; | |
// File:src/loaders/TextureLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.TextureLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.TextureLoader.prototype = { | |
constructor: THREE.TextureLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var texture = new THREE.Texture(); | |
var loader = new THREE.ImageLoader( this.manager ); | |
loader.setCrossOrigin( this.crossOrigin ); | |
loader.setPath( this.path ); | |
loader.load( url, function ( image ) { | |
texture.image = image; | |
texture.needsUpdate = true; | |
if ( onLoad !== undefined ) { | |
onLoad( texture ); | |
} | |
}, onProgress, onError ); | |
return texture; | |
}, | |
setCrossOrigin: function ( value ) { | |
this.crossOrigin = value; | |
}, | |
setPath: function ( value ) { | |
this.path = value; | |
} | |
}; | |
// File:src/loaders/CubeTextureLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.CubeTextureLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
}; | |
THREE.CubeTextureLoader.prototype = { | |
constructor: THREE.CubeTextureLoader, | |
load: function ( urls, onLoad, onProgress, onError ) { | |
var texture = new THREE.CubeTexture( [] ); | |
var loader = new THREE.ImageLoader( this.manager ); | |
loader.setCrossOrigin( this.crossOrigin ); | |
loader.setPath( this.path ); | |
var loaded = 0; | |
function loadTexture( i ) { | |
loader.load( urls[ i ], function ( image ) { | |
texture.images[ i ] = image; | |
loaded ++; | |
if ( loaded === 6 ) { | |
texture.needsUpdate = true; | |
if ( onLoad ) onLoad( texture ); | |
} | |
}, undefined, onError ); | |
} | |
for ( var i = 0; i < urls.length; ++ i ) { | |
loadTexture( i ); | |
} | |
return texture; | |
}, | |
setCrossOrigin: function ( value ) { | |
this.crossOrigin = value; | |
}, | |
setPath: function ( value ) { | |
this.path = value; | |
} | |
}; | |
// File:src/loaders/BinaryTextureLoader.js | |
/** | |
* @author Nikos M. / https://github.com/foo123/ | |
* | |
* Abstract Base class to load generic binary textures formats (rgbe, hdr, ...) | |
*/ | |
THREE.DataTextureLoader = THREE.BinaryTextureLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
// override in sub classes | |
this._parser = null; | |
}; | |
THREE.BinaryTextureLoader.prototype = { | |
constructor: THREE.BinaryTextureLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var texture = new THREE.DataTexture(); | |
var loader = new THREE.XHRLoader( this.manager ); | |
loader.setResponseType( 'arraybuffer' ); | |
loader.load( url, function ( buffer ) { | |
var texData = scope._parser( buffer ); | |
if ( ! texData ) return; | |
if ( undefined !== texData.image ) { | |
texture.image = texData.image; | |
} else if ( undefined !== texData.data ) { | |
texture.image.width = texData.width; | |
texture.image.height = texData.height; | |
texture.image.data = texData.data; | |
} | |
texture.wrapS = undefined !== texData.wrapS ? texData.wrapS : THREE.ClampToEdgeWrapping; | |
texture.wrapT = undefined !== texData.wrapT ? texData.wrapT : THREE.ClampToEdgeWrapping; | |
texture.magFilter = undefined !== texData.magFilter ? texData.magFilter : THREE.LinearFilter; | |
texture.minFilter = undefined !== texData.minFilter ? texData.minFilter : THREE.LinearMipMapLinearFilter; | |
texture.anisotropy = undefined !== texData.anisotropy ? texData.anisotropy : 1; | |
if ( undefined !== texData.format ) { | |
texture.format = texData.format; | |
} | |
if ( undefined !== texData.type ) { | |
texture.type = texData.type; | |
} | |
if ( undefined !== texData.mipmaps ) { | |
texture.mipmaps = texData.mipmaps; | |
} | |
if ( 1 === texData.mipmapCount ) { | |
texture.minFilter = THREE.LinearFilter; | |
} | |
texture.needsUpdate = true; | |
if ( onLoad ) onLoad( texture, texData ); | |
}, onProgress, onError ); | |
return texture; | |
} | |
}; | |
// File:src/loaders/CompressedTextureLoader.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* | |
* Abstract Base class to block based textures loader (dds, pvr, ...) | |
*/ | |
THREE.CompressedTextureLoader = function ( manager ) { | |
this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; | |
// override in sub classes | |
this._parser = null; | |
}; | |
THREE.CompressedTextureLoader.prototype = { | |
constructor: THREE.CompressedTextureLoader, | |
load: function ( url, onLoad, onProgress, onError ) { | |
var scope = this; | |
var images = []; | |
var texture = new THREE.CompressedTexture(); | |
texture.image = images; | |
var loader = new THREE.XHRLoader( this.manager ); | |
loader.setPath( this.path ); | |
loader.setResponseType( 'arraybuffer' ); | |
function loadTexture( i ) { | |
loader.load( url[ i ], function ( buffer ) { | |
var texDatas = scope._parser( buffer, true ); | |
images[ i ] = { | |
width: texDatas.width, | |
height: texDatas.height, | |
format: texDatas.format, | |
mipmaps: texDatas.mipmaps | |
}; | |
loaded += 1; | |
if ( loaded === 6 ) { | |
if ( texDatas.mipmapCount === 1 ) | |
texture.minFilter = THREE.LinearFilter; | |
texture.format = texDatas.format; | |
texture.needsUpdate = true; | |
if ( onLoad ) onLoad( texture ); | |
} | |
}, onProgress, onError ); | |
} | |
if ( Array.isArray( url ) ) { | |
var loaded = 0; | |
for ( var i = 0, il = url.length; i < il; ++ i ) { | |
loadTexture( i ); | |
} | |
} else { | |
// compressed cubemap texture stored in a single DDS file | |
loader.load( url, function ( buffer ) { | |
var texDatas = scope._parser( buffer, true ); | |
if ( texDatas.isCubemap ) { | |
var faces = texDatas.mipmaps.length / texDatas.mipmapCount; | |
for ( var f = 0; f < faces; f ++ ) { | |
images[ f ] = { mipmaps : [] }; | |
for ( var i = 0; i < texDatas.mipmapCount; i ++ ) { | |
images[ f ].mipmaps.push( texDatas.mipmaps[ f * texDatas.mipmapCount + i ] ); | |
images[ f ].format = texDatas.format; | |
images[ f ].width = texDatas.width; | |
images[ f ].height = texDatas.height; | |
} | |
} | |
} else { | |
texture.image.width = texDatas.width; | |
texture.image.height = texDatas.height; | |
texture.mipmaps = texDatas.mipmaps; | |
} | |
if ( texDatas.mipmapCount === 1 ) { | |
texture.minFilter = THREE.LinearFilter; | |
} | |
texture.format = texDatas.format; | |
texture.needsUpdate = true; | |
if ( onLoad ) onLoad( texture ); | |
}, onProgress, onError ); | |
} | |
return texture; | |
}, | |
setPath: function ( value ) { | |
this.path = value; | |
} | |
}; | |
// File:src/materials/Material.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Material = function () { | |
Object.defineProperty( this, 'id', { value: THREE.MaterialIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.type = 'Material'; | |
this.side = THREE.FrontSide; | |
this.opacity = 1; | |
this.transparent = false; | |
this.blending = THREE.NormalBlending; | |
this.blendSrc = THREE.SrcAlphaFactor; | |
this.blendDst = THREE.OneMinusSrcAlphaFactor; | |
this.blendEquation = THREE.AddEquation; | |
this.blendSrcAlpha = null; | |
this.blendDstAlpha = null; | |
this.blendEquationAlpha = null; | |
this.depthFunc = THREE.LessEqualDepth; | |
this.depthTest = true; | |
this.depthWrite = true; | |
this.colorWrite = true; | |
this.precision = null; // override the renderer's default precision for this material | |
this.polygonOffset = false; | |
this.polygonOffsetFactor = 0; | |
this.polygonOffsetUnits = 0; | |
this.alphaTest = 0; | |
this.overdraw = 0; // Overdrawn pixels (typically between 0 and 1) for fixing antialiasing gaps in CanvasRenderer | |
this.visible = true; | |
this._needsUpdate = true; | |
}; | |
THREE.Material.prototype = { | |
constructor: THREE.Material, | |
get needsUpdate () { | |
return this._needsUpdate; | |
}, | |
set needsUpdate ( value ) { | |
if ( value === true ) this.update(); | |
this._needsUpdate = value; | |
}, | |
setValues: function ( values ) { | |
if ( values === undefined ) return; | |
for ( var key in values ) { | |
var newValue = values[ key ]; | |
if ( newValue === undefined ) { | |
console.warn( "THREE.Material: '" + key + "' parameter is undefined." ); | |
continue; | |
} | |
var currentValue = this[ key ]; | |
if ( currentValue === undefined ) { | |
console.warn( "THREE." + this.type + ": '" + key + "' is not a property of this material." ); | |
continue; | |
} | |
if ( currentValue instanceof THREE.Color ) { | |
currentValue.set( newValue ); | |
} else if ( currentValue instanceof THREE.Vector3 && newValue instanceof THREE.Vector3 ) { | |
currentValue.copy( newValue ); | |
} else if ( key === 'overdraw' ) { | |
// ensure overdraw is backwards-compatible with legacy boolean type | |
this[ key ] = Number( newValue ); | |
} else { | |
this[ key ] = newValue; | |
} | |
} | |
}, | |
toJSON: function ( meta ) { | |
var isRoot = meta === undefined; | |
if ( isRoot ) { | |
meta = { | |
textures: {}, | |
images: {} | |
}; | |
} | |
var data = { | |
metadata: { | |
version: 4.4, | |
type: 'Material', | |
generator: 'Material.toJSON' | |
} | |
}; | |
// standard Material serialization | |
data.uuid = this.uuid; | |
data.type = this.type; | |
if ( this.name !== '' ) data.name = this.name; | |
if ( this.color instanceof THREE.Color ) data.color = this.color.getHex(); | |
if ( this.roughness !== 0.5 ) data.roughness = this.roughness; | |
if ( this.metalness !== 0.5 ) data.metalness = this.metalness; | |
if ( this.emissive instanceof THREE.Color ) data.emissive = this.emissive.getHex(); | |
if ( this.specular instanceof THREE.Color ) data.specular = this.specular.getHex(); | |
if ( this.shininess !== undefined ) data.shininess = this.shininess; | |
if ( this.map instanceof THREE.Texture ) data.map = this.map.toJSON( meta ).uuid; | |
if ( this.alphaMap instanceof THREE.Texture ) data.alphaMap = this.alphaMap.toJSON( meta ).uuid; | |
if ( this.lightMap instanceof THREE.Texture ) data.lightMap = this.lightMap.toJSON( meta ).uuid; | |
if ( this.bumpMap instanceof THREE.Texture ) { | |
data.bumpMap = this.bumpMap.toJSON( meta ).uuid; | |
data.bumpScale = this.bumpScale; | |
} | |
if ( this.normalMap instanceof THREE.Texture ) { | |
data.normalMap = this.normalMap.toJSON( meta ).uuid; | |
data.normalScale = this.normalScale.toArray(); | |
} | |
if ( this.displacementMap instanceof THREE.Texture ) { | |
data.displacementMap = this.displacementMap.toJSON( meta ).uuid; | |
data.displacementScale = this.displacementScale; | |
data.displacementBias = this.displacementBias; | |
} | |
if ( this.roughnessMap instanceof THREE.Texture ) data.roughnessMap = this.roughnessMap.toJSON( meta ).uuid; | |
if ( this.metalnessMap instanceof THREE.Texture ) data.metalnessMap = this.metalnessMap.toJSON( meta ).uuid; | |
if ( this.emissiveMap instanceof THREE.Texture ) data.emissiveMap = this.emissiveMap.toJSON( meta ).uuid; | |
if ( this.specularMap instanceof THREE.Texture ) data.specularMap = this.specularMap.toJSON( meta ).uuid; | |
if ( this.envMap instanceof THREE.Texture ) { | |
data.envMap = this.envMap.toJSON( meta ).uuid; | |
data.reflectivity = this.reflectivity; // Scale behind envMap | |
} | |
if ( this.size !== undefined ) data.size = this.size; | |
if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation; | |
if ( this.vertexColors !== undefined && this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors; | |
if ( this.shading !== undefined && this.shading !== THREE.SmoothShading ) data.shading = this.shading; | |
if ( this.blending !== undefined && this.blending !== THREE.NormalBlending ) data.blending = this.blending; | |
if ( this.side !== undefined && this.side !== THREE.FrontSide ) data.side = this.side; | |
if ( this.opacity < 1 ) data.opacity = this.opacity; | |
if ( this.transparent === true ) data.transparent = this.transparent; | |
if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest; | |
if ( this.wireframe === true ) data.wireframe = this.wireframe; | |
if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth; | |
// TODO: Copied from Object3D.toJSON | |
function extractFromCache ( cache ) { | |
var values = []; | |
for ( var key in cache ) { | |
var data = cache[ key ]; | |
delete data.metadata; | |
values.push( data ); | |
} | |
return values; | |
} | |
if ( isRoot ) { | |
var textures = extractFromCache( meta.textures ); | |
var images = extractFromCache( meta.images ); | |
if ( textures.length > 0 ) data.textures = textures; | |
if ( images.length > 0 ) data.images = images; | |
} | |
return data; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( source ) { | |
this.name = source.name; | |
this.side = source.side; | |
this.opacity = source.opacity; | |
this.transparent = source.transparent; | |
this.blending = source.blending; | |
this.blendSrc = source.blendSrc; | |
this.blendDst = source.blendDst; | |
this.blendEquation = source.blendEquation; | |
this.blendSrcAlpha = source.blendSrcAlpha; | |
this.blendDstAlpha = source.blendDstAlpha; | |
this.blendEquationAlpha = source.blendEquationAlpha; | |
this.depthFunc = source.depthFunc; | |
this.depthTest = source.depthTest; | |
this.depthWrite = source.depthWrite; | |
this.colorWrite = source.colorWrite; | |
this.precision = source.precision; | |
this.polygonOffset = source.polygonOffset; | |
this.polygonOffsetFactor = source.polygonOffsetFactor; | |
this.polygonOffsetUnits = source.polygonOffsetUnits; | |
this.alphaTest = source.alphaTest; | |
this.overdraw = source.overdraw; | |
this.visible = source.visible; | |
return this; | |
}, | |
update: function () { | |
this.dispatchEvent( { type: 'update' } ); | |
}, | |
dispose: function () { | |
this.dispatchEvent( { type: 'dispose' } ); | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.Material.prototype ); | |
THREE.MaterialIdCount = 0; | |
// File:src/materials/LineBasicMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* linewidth: <float>, | |
* linecap: "round", | |
* linejoin: "round", | |
* | |
* vertexColors: <bool> | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.LineBasicMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'LineBasicMaterial'; | |
this.color = new THREE.Color( 0xffffff ); | |
this.linewidth = 1; | |
this.linecap = 'round'; | |
this.linejoin = 'round'; | |
this.vertexColors = THREE.NoColors; | |
this.fog = true; | |
this.setValues( parameters ); | |
}; | |
THREE.LineBasicMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.LineBasicMaterial.prototype.constructor = THREE.LineBasicMaterial; | |
THREE.LineBasicMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.linewidth = source.linewidth; | |
this.linecap = source.linecap; | |
this.linejoin = source.linejoin; | |
this.vertexColors = source.vertexColors; | |
this.fog = source.fog; | |
return this; | |
}; | |
// File:src/materials/LineDashedMaterial.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* linewidth: <float>, | |
* | |
* scale: <float>, | |
* dashSize: <float>, | |
* gapSize: <float>, | |
* | |
* vertexColors: THREE.NoColors / THREE.FaceColors / THREE.VertexColors | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.LineDashedMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'LineDashedMaterial'; | |
this.color = new THREE.Color( 0xffffff ); | |
this.linewidth = 1; | |
this.scale = 1; | |
this.dashSize = 3; | |
this.gapSize = 1; | |
this.vertexColors = THREE.NoColors; | |
this.fog = true; | |
this.setValues( parameters ); | |
}; | |
THREE.LineDashedMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.LineDashedMaterial.prototype.constructor = THREE.LineDashedMaterial; | |
THREE.LineDashedMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.linewidth = source.linewidth; | |
this.scale = source.scale; | |
this.dashSize = source.dashSize; | |
this.gapSize = source.gapSize; | |
this.vertexColors = source.vertexColors; | |
this.fog = source.fog; | |
return this; | |
}; | |
// File:src/materials/MeshBasicMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* map: new THREE.Texture( <Image> ), | |
* | |
* aoMap: new THREE.Texture( <Image> ), | |
* aoMapIntensity: <float> | |
* | |
* specularMap: new THREE.Texture( <Image> ), | |
* | |
* alphaMap: new THREE.Texture( <Image> ), | |
* | |
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), | |
* combine: THREE.Multiply, | |
* reflectivity: <float>, | |
* refractionRatio: <float>, | |
* | |
* shading: THREE.SmoothShading, | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float>, | |
* | |
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, | |
* | |
* skinning: <bool>, | |
* morphTargets: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.MeshBasicMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'MeshBasicMaterial'; | |
this.color = new THREE.Color( 0xffffff ); // emissive | |
this.map = null; | |
this.aoMap = null; | |
this.aoMapIntensity = 1.0; | |
this.specularMap = null; | |
this.alphaMap = null; | |
this.envMap = null; | |
this.combine = THREE.MultiplyOperation; | |
this.reflectivity = 1; | |
this.refractionRatio = 0.98; | |
this.fog = true; | |
this.shading = THREE.SmoothShading; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.wireframeLinecap = 'round'; | |
this.wireframeLinejoin = 'round'; | |
this.vertexColors = THREE.NoColors; | |
this.skinning = false; | |
this.morphTargets = false; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshBasicMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshBasicMaterial.prototype.constructor = THREE.MeshBasicMaterial; | |
THREE.MeshBasicMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.map = source.map; | |
this.aoMap = source.aoMap; | |
this.aoMapIntensity = source.aoMapIntensity; | |
this.specularMap = source.specularMap; | |
this.alphaMap = source.alphaMap; | |
this.envMap = source.envMap; | |
this.combine = source.combine; | |
this.reflectivity = source.reflectivity; | |
this.refractionRatio = source.refractionRatio; | |
this.fog = source.fog; | |
this.shading = source.shading; | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
this.wireframeLinecap = source.wireframeLinecap; | |
this.wireframeLinejoin = source.wireframeLinejoin; | |
this.vertexColors = source.vertexColors; | |
this.skinning = source.skinning; | |
this.morphTargets = source.morphTargets; | |
return this; | |
}; | |
// File:src/materials/MeshLambertMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* | |
* map: new THREE.Texture( <Image> ), | |
* | |
* lightMap: new THREE.Texture( <Image> ), | |
* lightMapIntensity: <float> | |
* | |
* aoMap: new THREE.Texture( <Image> ), | |
* aoMapIntensity: <float> | |
* | |
* emissive: <hex>, | |
* emissiveIntensity: <float> | |
* emissiveMap: new THREE.Texture( <Image> ), | |
* | |
* specularMap: new THREE.Texture( <Image> ), | |
* | |
* alphaMap: new THREE.Texture( <Image> ), | |
* | |
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), | |
* combine: THREE.Multiply, | |
* reflectivity: <float>, | |
* refractionRatio: <float>, | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float>, | |
* | |
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, | |
* | |
* skinning: <bool>, | |
* morphTargets: <bool>, | |
* morphNormals: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.MeshLambertMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'MeshLambertMaterial'; | |
this.color = new THREE.Color( 0xffffff ); // diffuse | |
this.map = null; | |
this.lightMap = null; | |
this.lightMapIntensity = 1.0; | |
this.aoMap = null; | |
this.aoMapIntensity = 1.0; | |
this.emissive = new THREE.Color( 0x000000 ); | |
this.emissiveIntensity = 1.0; | |
this.emissiveMap = null; | |
this.specularMap = null; | |
this.alphaMap = null; | |
this.envMap = null; | |
this.combine = THREE.MultiplyOperation; | |
this.reflectivity = 1; | |
this.refractionRatio = 0.98; | |
this.fog = true; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.wireframeLinecap = 'round'; | |
this.wireframeLinejoin = 'round'; | |
this.vertexColors = THREE.NoColors; | |
this.skinning = false; | |
this.morphTargets = false; | |
this.morphNormals = false; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshLambertMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshLambertMaterial.prototype.constructor = THREE.MeshLambertMaterial; | |
THREE.MeshLambertMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.map = source.map; | |
this.lightMap = source.lightMap; | |
this.lightMapIntensity = source.lightMapIntensity; | |
this.aoMap = source.aoMap; | |
this.aoMapIntensity = source.aoMapIntensity; | |
this.emissive.copy( source.emissive ); | |
this.emissiveMap = source.emissiveMap; | |
this.emissiveIntensity = source.emissiveIntensity; | |
this.specularMap = source.specularMap; | |
this.alphaMap = source.alphaMap; | |
this.envMap = source.envMap; | |
this.combine = source.combine; | |
this.reflectivity = source.reflectivity; | |
this.refractionRatio = source.refractionRatio; | |
this.fog = source.fog; | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
this.wireframeLinecap = source.wireframeLinecap; | |
this.wireframeLinejoin = source.wireframeLinejoin; | |
this.vertexColors = source.vertexColors; | |
this.skinning = source.skinning; | |
this.morphTargets = source.morphTargets; | |
this.morphNormals = source.morphNormals; | |
return this; | |
}; | |
// File:src/materials/MeshPhongMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* specular: <hex>, | |
* shininess: <float>, | |
* opacity: <float>, | |
* | |
* map: new THREE.Texture( <Image> ), | |
* | |
* lightMap: new THREE.Texture( <Image> ), | |
* lightMapIntensity: <float> | |
* | |
* aoMap: new THREE.Texture( <Image> ), | |
* aoMapIntensity: <float> | |
* | |
* emissive: <hex>, | |
* emissiveIntensity: <float> | |
* emissiveMap: new THREE.Texture( <Image> ), | |
* | |
* bumpMap: new THREE.Texture( <Image> ), | |
* bumpScale: <float>, | |
* | |
* normalMap: new THREE.Texture( <Image> ), | |
* normalScale: <Vector2>, | |
* | |
* displacementMap: new THREE.Texture( <Image> ), | |
* displacementScale: <float>, | |
* displacementBias: <float>, | |
* | |
* specularMap: new THREE.Texture( <Image> ), | |
* | |
* alphaMap: new THREE.Texture( <Image> ), | |
* | |
* envMap: new THREE.TextureCube( [posx, negx, posy, negy, posz, negz] ), | |
* combine: THREE.Multiply, | |
* reflectivity: <float>, | |
* refractionRatio: <float>, | |
* | |
* shading: THREE.SmoothShading, | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float>, | |
* | |
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, | |
* | |
* skinning: <bool>, | |
* morphTargets: <bool>, | |
* morphNormals: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.MeshPhongMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'MeshPhongMaterial'; | |
this.color = new THREE.Color( 0xffffff ); // diffuse | |
this.specular = new THREE.Color( 0x111111 ); | |
this.shininess = 30; | |
this.map = null; | |
this.lightMap = null; | |
this.lightMapIntensity = 1.0; | |
this.aoMap = null; | |
this.aoMapIntensity = 1.0; | |
this.emissive = new THREE.Color( 0x000000 ); | |
this.emissiveIntensity = 1.0; | |
this.emissiveMap = null; | |
this.bumpMap = null; | |
this.bumpScale = 1; | |
this.normalMap = null; | |
this.normalScale = new THREE.Vector2( 1, 1 ); | |
this.displacementMap = null; | |
this.displacementScale = 1; | |
this.displacementBias = 0; | |
this.specularMap = null; | |
this.alphaMap = null; | |
this.envMap = null; | |
this.combine = THREE.MultiplyOperation; | |
this.reflectivity = 1; | |
this.refractionRatio = 0.98; | |
this.fog = true; | |
this.shading = THREE.SmoothShading; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.wireframeLinecap = 'round'; | |
this.wireframeLinejoin = 'round'; | |
this.vertexColors = THREE.NoColors; | |
this.skinning = false; | |
this.morphTargets = false; | |
this.morphNormals = false; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshPhongMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshPhongMaterial.prototype.constructor = THREE.MeshPhongMaterial; | |
THREE.MeshPhongMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.specular.copy( source.specular ); | |
this.shininess = source.shininess; | |
this.map = source.map; | |
this.lightMap = source.lightMap; | |
this.lightMapIntensity = source.lightMapIntensity; | |
this.aoMap = source.aoMap; | |
this.aoMapIntensity = source.aoMapIntensity; | |
this.emissive.copy( source.emissive ); | |
this.emissiveMap = source.emissiveMap; | |
this.emissiveIntensity = source.emissiveIntensity; | |
this.bumpMap = source.bumpMap; | |
this.bumpScale = source.bumpScale; | |
this.normalMap = source.normalMap; | |
this.normalScale.copy( source.normalScale ); | |
this.displacementMap = source.displacementMap; | |
this.displacementScale = source.displacementScale; | |
this.displacementBias = source.displacementBias; | |
this.specularMap = source.specularMap; | |
this.alphaMap = source.alphaMap; | |
this.envMap = source.envMap; | |
this.combine = source.combine; | |
this.reflectivity = source.reflectivity; | |
this.refractionRatio = source.refractionRatio; | |
this.fog = source.fog; | |
this.shading = source.shading; | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
this.wireframeLinecap = source.wireframeLinecap; | |
this.wireframeLinejoin = source.wireframeLinejoin; | |
this.vertexColors = source.vertexColors; | |
this.skinning = source.skinning; | |
this.morphTargets = source.morphTargets; | |
this.morphNormals = source.morphNormals; | |
return this; | |
}; | |
// File:src/materials/MeshStandardMaterial.js | |
/** | |
* @author WestLangley / http://github.com/WestLangley | |
* | |
* parameters = { | |
* color: <hex>, | |
* roughness: <float>, | |
* metalness: <float>, | |
* opacity: <float>, | |
* | |
* map: new THREE.Texture( <Image> ), | |
* | |
* lightMap: new THREE.Texture( <Image> ), | |
* lightMapIntensity: <float> | |
* | |
* aoMap: new THREE.Texture( <Image> ), | |
* aoMapIntensity: <float> | |
* | |
* emissive: <hex>, | |
* emissiveIntensity: <float> | |
* emissiveMap: new THREE.Texture( <Image> ), | |
* | |
* bumpMap: new THREE.Texture( <Image> ), | |
* bumpScale: <float>, | |
* | |
* normalMap: new THREE.Texture( <Image> ), | |
* normalScale: <Vector2>, | |
* | |
* displacementMap: new THREE.Texture( <Image> ), | |
* displacementScale: <float>, | |
* displacementBias: <float>, | |
* | |
* roughnessMap: new THREE.Texture( <Image> ), | |
* | |
* metalnessMap: new THREE.Texture( <Image> ), | |
* | |
* alphaMap: new THREE.Texture( <Image> ), | |
* | |
* envMap: new THREE.CubeTexture( [posx, negx, posy, negy, posz, negz] ), | |
* envMapIntensity: <float> | |
* | |
* refractionRatio: <float>, | |
* | |
* shading: THREE.SmoothShading, | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float>, | |
* | |
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, | |
* | |
* skinning: <bool>, | |
* morphTargets: <bool>, | |
* morphNormals: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.MeshStandardMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'MeshStandardMaterial'; | |
this.color = new THREE.Color( 0xffffff ); // diffuse | |
this.roughness = 0.5; | |
this.metalness = 0.5; | |
this.map = null; | |
this.lightMap = null; | |
this.lightMapIntensity = 1.0; | |
this.aoMap = null; | |
this.aoMapIntensity = 1.0; | |
this.emissive = new THREE.Color( 0x000000 ); | |
this.emissiveIntensity = 1.0; | |
this.emissiveMap = null; | |
this.bumpMap = null; | |
this.bumpScale = 1; | |
this.normalMap = null; | |
this.normalScale = new THREE.Vector2( 1, 1 ); | |
this.displacementMap = null; | |
this.displacementScale = 1; | |
this.displacementBias = 0; | |
this.roughnessMap = null; | |
this.metalnessMap = null; | |
this.alphaMap = null; | |
this.envMap = null; | |
this.envMapIntensity = 1.0; | |
this.refractionRatio = 0.98; | |
this.fog = true; | |
this.shading = THREE.SmoothShading; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.wireframeLinecap = 'round'; | |
this.wireframeLinejoin = 'round'; | |
this.vertexColors = THREE.NoColors; | |
this.skinning = false; | |
this.morphTargets = false; | |
this.morphNormals = false; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshStandardMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshStandardMaterial.prototype.constructor = THREE.MeshStandardMaterial; | |
THREE.MeshStandardMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.roughness = source.roughness; | |
this.metalness = source.metalness; | |
this.map = source.map; | |
this.lightMap = source.lightMap; | |
this.lightMapIntensity = source.lightMapIntensity; | |
this.aoMap = source.aoMap; | |
this.aoMapIntensity = source.aoMapIntensity; | |
this.emissive.copy( source.emissive ); | |
this.emissiveMap = source.emissiveMap; | |
this.emissiveIntensity = source.emissiveIntensity; | |
this.bumpMap = source.bumpMap; | |
this.bumpScale = source.bumpScale; | |
this.normalMap = source.normalMap; | |
this.normalScale.copy( source.normalScale ); | |
this.displacementMap = source.displacementMap; | |
this.displacementScale = source.displacementScale; | |
this.displacementBias = source.displacementBias; | |
this.roughnessMap = source.roughnessMap; | |
this.metalnessMap = source.metalnessMap; | |
this.alphaMap = source.alphaMap; | |
this.envMap = source.envMap; | |
this.envMapIntensity = source.envMapIntensity; | |
this.refractionRatio = source.refractionRatio; | |
this.fog = source.fog; | |
this.shading = source.shading; | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
this.wireframeLinecap = source.wireframeLinecap; | |
this.wireframeLinejoin = source.wireframeLinejoin; | |
this.vertexColors = source.vertexColors; | |
this.skinning = source.skinning; | |
this.morphTargets = source.morphTargets; | |
this.morphNormals = source.morphNormals; | |
return this; | |
}; | |
// File:src/materials/MeshDepthMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* opacity: <float>, | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float> | |
* } | |
*/ | |
THREE.MeshDepthMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'MeshDepthMaterial'; | |
this.morphTargets = false; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshDepthMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshDepthMaterial.prototype.constructor = THREE.MeshDepthMaterial; | |
THREE.MeshDepthMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
return this; | |
}; | |
// File:src/materials/MeshNormalMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* | |
* parameters = { | |
* opacity: <float>, | |
* | |
* shading: THREE.FlatShading, | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float> | |
* } | |
*/ | |
THREE.MeshNormalMaterial = function ( parameters ) { | |
THREE.Material.call( this, parameters ); | |
this.type = 'MeshNormalMaterial'; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.morphTargets = false; | |
this.setValues( parameters ); | |
}; | |
THREE.MeshNormalMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.MeshNormalMaterial.prototype.constructor = THREE.MeshNormalMaterial; | |
THREE.MeshNormalMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
return this; | |
}; | |
// File:src/materials/MultiMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.MultiMaterial = function ( materials ) { | |
this.uuid = THREE.Math.generateUUID(); | |
this.type = 'MultiMaterial'; | |
this.materials = materials instanceof Array ? materials : []; | |
this.visible = true; | |
}; | |
THREE.MultiMaterial.prototype = { | |
constructor: THREE.MultiMaterial, | |
toJSON: function ( meta ) { | |
var output = { | |
metadata: { | |
version: 4.2, | |
type: 'material', | |
generator: 'MaterialExporter' | |
}, | |
uuid: this.uuid, | |
type: this.type, | |
materials: [] | |
}; | |
var materials = this.materials; | |
for ( var i = 0, l = materials.length; i < l; i ++ ) { | |
var material = materials[ i ].toJSON( meta ); | |
delete material.metadata; | |
output.materials.push( material ); | |
} | |
output.visible = this.visible; | |
return output; | |
}, | |
clone: function () { | |
var material = new this.constructor(); | |
for ( var i = 0; i < this.materials.length; i ++ ) { | |
material.materials.push( this.materials[ i ].clone() ); | |
} | |
material.visible = this.visible; | |
return material; | |
} | |
}; | |
// File:src/materials/PointsMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* map: new THREE.Texture( <Image> ), | |
* | |
* size: <float>, | |
* sizeAttenuation: <bool>, | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* vertexColors: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.PointsMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'PointsMaterial'; | |
this.color = new THREE.Color( 0xffffff ); | |
this.map = null; | |
this.size = 1; | |
this.sizeAttenuation = true; | |
this.vertexColors = THREE.NoColors; | |
this.fog = true; | |
this.setValues( parameters ); | |
}; | |
THREE.PointsMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.PointsMaterial.prototype.constructor = THREE.PointsMaterial; | |
THREE.PointsMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.map = source.map; | |
this.size = source.size; | |
this.sizeAttenuation = source.sizeAttenuation; | |
this.vertexColors = source.vertexColors; | |
this.fog = source.fog; | |
return this; | |
}; | |
// File:src/materials/ShaderMaterial.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* defines: { "label" : "value" }, | |
* uniforms: { "parameter1": { type: "f", value: 1.0 }, "parameter2": { type: "i" value2: 2 } }, | |
* | |
* fragmentShader: <string>, | |
* vertexShader: <string>, | |
* | |
* shading: THREE.SmoothShading, | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* wireframe: <boolean>, | |
* wireframeLinewidth: <float>, | |
* | |
* lights: <bool>, | |
* | |
* vertexColors: THREE.NoColors / THREE.VertexColors / THREE.FaceColors, | |
* | |
* skinning: <bool>, | |
* morphTargets: <bool>, | |
* morphNormals: <bool>, | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.ShaderMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'ShaderMaterial'; | |
this.defines = {}; | |
this.uniforms = {}; | |
this.vertexShader = 'void main() {\n\tgl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n}'; | |
this.fragmentShader = 'void main() {\n\tgl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );\n}'; | |
this.shading = THREE.SmoothShading; | |
this.linewidth = 1; | |
this.wireframe = false; | |
this.wireframeLinewidth = 1; | |
this.fog = false; // set to use scene fog | |
this.lights = false; // set to use scene lights | |
this.vertexColors = THREE.NoColors; // set to use "color" attribute stream | |
this.skinning = false; // set to use skinning attribute streams | |
this.morphTargets = false; // set to use morph targets | |
this.morphNormals = false; // set to use morph normals | |
this.extensions = { | |
derivatives: false, // set to use derivatives | |
fragDepth: false, // set to use fragment depth values | |
drawBuffers: false, // set to use draw buffers | |
shaderTextureLOD: false // set to use shader texture LOD | |
}; | |
// When rendered geometry doesn't include these attributes but the material does, | |
// use these default values in WebGL. This avoids errors when buffer data is missing. | |
this.defaultAttributeValues = { | |
'color': [ 1, 1, 1 ], | |
'uv': [ 0, 0 ], | |
'uv2': [ 0, 0 ] | |
}; | |
this.index0AttributeName = undefined; | |
if ( parameters !== undefined ) { | |
if ( parameters.attributes !== undefined ) { | |
console.error( 'THREE.ShaderMaterial: attributes should now be defined in THREE.BufferGeometry instead.' ); | |
} | |
this.setValues( parameters ); | |
} | |
}; | |
THREE.ShaderMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.ShaderMaterial.prototype.constructor = THREE.ShaderMaterial; | |
THREE.ShaderMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.fragmentShader = source.fragmentShader; | |
this.vertexShader = source.vertexShader; | |
this.uniforms = THREE.UniformsUtils.clone( source.uniforms ); | |
this.defines = source.defines; | |
this.shading = source.shading; | |
this.wireframe = source.wireframe; | |
this.wireframeLinewidth = source.wireframeLinewidth; | |
this.fog = source.fog; | |
this.lights = source.lights; | |
this.vertexColors = source.vertexColors; | |
this.skinning = source.skinning; | |
this.morphTargets = source.morphTargets; | |
this.morphNormals = source.morphNormals; | |
this.extensions = source.extensions; | |
return this; | |
}; | |
THREE.ShaderMaterial.prototype.toJSON = function ( meta ) { | |
var data = THREE.Material.prototype.toJSON.call( this, meta ); | |
data.uniforms = this.uniforms; | |
data.vertexShader = this.vertexShader; | |
data.fragmentShader = this.fragmentShader; | |
return data; | |
}; | |
// File:src/materials/RawShaderMaterial.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.RawShaderMaterial = function ( parameters ) { | |
THREE.ShaderMaterial.call( this, parameters ); | |
this.type = 'RawShaderMaterial'; | |
}; | |
THREE.RawShaderMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype ); | |
THREE.RawShaderMaterial.prototype.constructor = THREE.RawShaderMaterial; | |
// File:src/materials/SpriteMaterial.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
* | |
* parameters = { | |
* color: <hex>, | |
* opacity: <float>, | |
* map: new THREE.Texture( <Image> ), | |
* | |
* blending: THREE.NormalBlending, | |
* depthTest: <bool>, | |
* depthWrite: <bool>, | |
* | |
* uvOffset: new THREE.Vector2(), | |
* uvScale: new THREE.Vector2(), | |
* | |
* fog: <bool> | |
* } | |
*/ | |
THREE.SpriteMaterial = function ( parameters ) { | |
THREE.Material.call( this ); | |
this.type = 'SpriteMaterial'; | |
this.color = new THREE.Color( 0xffffff ); | |
this.map = null; | |
this.rotation = 0; | |
this.fog = false; | |
// set parameters | |
this.setValues( parameters ); | |
}; | |
THREE.SpriteMaterial.prototype = Object.create( THREE.Material.prototype ); | |
THREE.SpriteMaterial.prototype.constructor = THREE.SpriteMaterial; | |
THREE.SpriteMaterial.prototype.copy = function ( source ) { | |
THREE.Material.prototype.copy.call( this, source ); | |
this.color.copy( source.color ); | |
this.map = source.map; | |
this.rotation = source.rotation; | |
this.fog = source.fog; | |
return this; | |
}; | |
// File:src/textures/Texture.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author szimek / https://github.com/szimek/ | |
*/ | |
THREE.Texture = function ( image, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
Object.defineProperty( this, 'id', { value: THREE.TextureIdCount ++ } ); | |
this.uuid = THREE.Math.generateUUID(); | |
this.name = ''; | |
this.sourceFile = ''; | |
this.image = image !== undefined ? image : THREE.Texture.DEFAULT_IMAGE; | |
this.mipmaps = []; | |
this.mapping = mapping !== undefined ? mapping : THREE.Texture.DEFAULT_MAPPING; | |
this.wrapS = wrapS !== undefined ? wrapS : THREE.ClampToEdgeWrapping; | |
this.wrapT = wrapT !== undefined ? wrapT : THREE.ClampToEdgeWrapping; | |
this.magFilter = magFilter !== undefined ? magFilter : THREE.LinearFilter; | |
this.minFilter = minFilter !== undefined ? minFilter : THREE.LinearMipMapLinearFilter; | |
this.anisotropy = anisotropy !== undefined ? anisotropy : 1; | |
this.format = format !== undefined ? format : THREE.RGBAFormat; | |
this.type = type !== undefined ? type : THREE.UnsignedByteType; | |
this.offset = new THREE.Vector2( 0, 0 ); | |
this.repeat = new THREE.Vector2( 1, 1 ); | |
this.generateMipmaps = true; | |
this.premultiplyAlpha = false; | |
this.flipY = true; | |
this.unpackAlignment = 4; // valid values: 1, 2, 4, 8 (see http://www.khronos.org/opengles/sdk/docs/man/xhtml/glPixelStorei.xml) | |
this.version = 0; | |
this.onUpdate = null; | |
}; | |
THREE.Texture.DEFAULT_IMAGE = undefined; | |
THREE.Texture.DEFAULT_MAPPING = THREE.UVMapping; | |
THREE.Texture.prototype = { | |
constructor: THREE.Texture, | |
set needsUpdate ( value ) { | |
if ( value === true ) this.version ++; | |
}, | |
clone: function () { | |
return new this.constructor().copy( this ); | |
}, | |
copy: function ( source ) { | |
this.image = source.image; | |
this.mipmaps = source.mipmaps.slice( 0 ); | |
this.mapping = source.mapping; | |
this.wrapS = source.wrapS; | |
this.wrapT = source.wrapT; | |
this.magFilter = source.magFilter; | |
this.minFilter = source.minFilter; | |
this.anisotropy = source.anisotropy; | |
this.format = source.format; | |
this.type = source.type; | |
this.offset.copy( source.offset ); | |
this.repeat.copy( source.repeat ); | |
this.generateMipmaps = source.generateMipmaps; | |
this.premultiplyAlpha = source.premultiplyAlpha; | |
this.flipY = source.flipY; | |
this.unpackAlignment = source.unpackAlignment; | |
return this; | |
}, | |
toJSON: function ( meta ) { | |
if ( meta.textures[ this.uuid ] !== undefined ) { | |
return meta.textures[ this.uuid ]; | |
} | |
function getDataURL( image ) { | |
var canvas; | |
if ( image.toDataURL !== undefined ) { | |
canvas = image; | |
} else { | |
canvas = document.createElement( 'canvas' ); | |
canvas.width = image.width; | |
canvas.height = image.height; | |
canvas.getContext( '2d' ).drawImage( image, 0, 0, image.width, image.height ); | |
} | |
if ( canvas.width > 2048 || canvas.height > 2048 ) { | |
return canvas.toDataURL( 'image/jpeg', 0.6 ); | |
} else { | |
return canvas.toDataURL( 'image/png' ); | |
} | |
} | |
var output = { | |
metadata: { | |
version: 4.4, | |
type: 'Texture', | |
generator: 'Texture.toJSON' | |
}, | |
uuid: this.uuid, | |
name: this.name, | |
mapping: this.mapping, | |
repeat: [ this.repeat.x, this.repeat.y ], | |
offset: [ this.offset.x, this.offset.y ], | |
wrap: [ this.wrapS, this.wrapT ], | |
minFilter: this.minFilter, | |
magFilter: this.magFilter, | |
anisotropy: this.anisotropy | |
}; | |
if ( this.image !== undefined ) { | |
// TODO: Move to THREE.Image | |
var image = this.image; | |
if ( image.uuid === undefined ) { | |
image.uuid = THREE.Math.generateUUID(); // UGH | |
} | |
if ( meta.images[ image.uuid ] === undefined ) { | |
meta.images[ image.uuid ] = { | |
uuid: image.uuid, | |
url: getDataURL( image ) | |
}; | |
} | |
output.image = image.uuid; | |
} | |
meta.textures[ this.uuid ] = output; | |
return output; | |
}, | |
dispose: function () { | |
this.dispatchEvent( { type: 'dispose' } ); | |
}, | |
transformUv: function ( uv ) { | |
if ( this.mapping !== THREE.UVMapping ) return; | |
uv.multiply( this.repeat ); | |
uv.add( this.offset ); | |
if ( uv.x < 0 || uv.x > 1 ) { | |
switch ( this.wrapS ) { | |
case THREE.RepeatWrapping: | |
uv.x = uv.x - Math.floor( uv.x ); | |
break; | |
case THREE.ClampToEdgeWrapping: | |
uv.x = uv.x < 0 ? 0 : 1; | |
break; | |
case THREE.MirroredRepeatWrapping: | |
if ( Math.abs( Math.floor( uv.x ) % 2 ) === 1 ) { | |
uv.x = Math.ceil( uv.x ) - uv.x; | |
} else { | |
uv.x = uv.x - Math.floor( uv.x ); | |
} | |
break; | |
} | |
} | |
if ( uv.y < 0 || uv.y > 1 ) { | |
switch ( this.wrapT ) { | |
case THREE.RepeatWrapping: | |
uv.y = uv.y - Math.floor( uv.y ); | |
break; | |
case THREE.ClampToEdgeWrapping: | |
uv.y = uv.y < 0 ? 0 : 1; | |
break; | |
case THREE.MirroredRepeatWrapping: | |
if ( Math.abs( Math.floor( uv.y ) % 2 ) === 1 ) { | |
uv.y = Math.ceil( uv.y ) - uv.y; | |
} else { | |
uv.y = uv.y - Math.floor( uv.y ); | |
} | |
break; | |
} | |
} | |
if ( this.flipY ) { | |
uv.y = 1 - uv.y; | |
} | |
} | |
}; | |
THREE.EventDispatcher.prototype.apply( THREE.Texture.prototype ); | |
THREE.TextureIdCount = 0; | |
// File:src/textures/CanvasTexture.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.CanvasTexture = function ( canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
THREE.Texture.call( this, canvas, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
this.needsUpdate = true; | |
}; | |
THREE.CanvasTexture.prototype = Object.create( THREE.Texture.prototype ); | |
THREE.CanvasTexture.prototype.constructor = THREE.CanvasTexture; | |
// File:src/textures/CubeTexture.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.CubeTexture = function ( images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
mapping = mapping !== undefined ? mapping : THREE.CubeReflectionMapping; | |
THREE.Texture.call( this, images, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
this.images = images; | |
this.flipY = false; | |
}; | |
THREE.CubeTexture.prototype = Object.create( THREE.Texture.prototype ); | |
THREE.CubeTexture.prototype.constructor = THREE.CubeTexture; | |
THREE.CubeTexture.prototype.copy = function ( source ) { | |
THREE.Texture.prototype.copy.call( this, source ); | |
this.images = source.images; | |
return this; | |
}; | |
// File:src/textures/CompressedTexture.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.CompressedTexture = function ( mipmaps, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { | |
THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
this.image = { width: width, height: height }; | |
this.mipmaps = mipmaps; | |
// no flipping for cube textures | |
// (also flipping doesn't work for compressed textures ) | |
this.flipY = false; | |
// can't generate mipmaps for compressed textures | |
// mips must be embedded in DDS files | |
this.generateMipmaps = false; | |
}; | |
THREE.CompressedTexture.prototype = Object.create( THREE.Texture.prototype ); | |
THREE.CompressedTexture.prototype.constructor = THREE.CompressedTexture; | |
// File:src/textures/DataTexture.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.DataTexture = function ( data, width, height, format, type, mapping, wrapS, wrapT, magFilter, minFilter, anisotropy ) { | |
THREE.Texture.call( this, null, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
this.image = { data: data, width: width, height: height }; | |
this.magFilter = magFilter !== undefined ? magFilter : THREE.NearestFilter; | |
this.minFilter = minFilter !== undefined ? minFilter : THREE.NearestFilter; | |
this.flipY = false; | |
this.generateMipmaps = false; | |
}; | |
THREE.DataTexture.prototype = Object.create( THREE.Texture.prototype ); | |
THREE.DataTexture.prototype.constructor = THREE.DataTexture; | |
// File:src/textures/VideoTexture.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.VideoTexture = function ( video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ) { | |
THREE.Texture.call( this, video, mapping, wrapS, wrapT, magFilter, minFilter, format, type, anisotropy ); | |
this.generateMipmaps = false; | |
var scope = this; | |
function update() { | |
requestAnimationFrame( update ); | |
if ( video.readyState === video.HAVE_ENOUGH_DATA ) { | |
scope.needsUpdate = true; | |
} | |
} | |
update(); | |
}; | |
THREE.VideoTexture.prototype = Object.create( THREE.Texture.prototype ); | |
THREE.VideoTexture.prototype.constructor = THREE.VideoTexture; | |
// File:src/objects/Group.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Group = function () { | |
THREE.Object3D.call( this ); | |
this.type = 'Group'; | |
}; | |
THREE.Group.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Group.prototype.constructor = THREE.Group; | |
// File:src/objects/Points.js | |
/** | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Points = function ( geometry, material ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Points'; | |
this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); | |
this.material = material !== undefined ? material : new THREE.PointsMaterial( { color: Math.random() * 0xffffff } ); | |
}; | |
THREE.Points.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Points.prototype.constructor = THREE.Points; | |
THREE.Points.prototype.raycast = ( function () { | |
var inverseMatrix = new THREE.Matrix4(); | |
var ray = new THREE.Ray(); | |
var sphere = new THREE.Sphere(); | |
return function raycast( raycaster, intersects ) { | |
var object = this; | |
var geometry = this.geometry; | |
var matrixWorld = this.matrixWorld; | |
var threshold = raycaster.params.Points.threshold; | |
// Checking boundingSphere distance to ray | |
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
sphere.copy( geometry.boundingSphere ); | |
sphere.applyMatrix4( matrixWorld ); | |
if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
// | |
inverseMatrix.getInverse( matrixWorld ); | |
ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
var localThreshold = threshold / ( ( this.scale.x + this.scale.y + this.scale.z ) / 3 ); | |
var localThresholdSq = localThreshold * localThreshold; | |
var position = new THREE.Vector3(); | |
function testPoint( point, index ) { | |
var rayPointDistanceSq = ray.distanceSqToPoint( point ); | |
if ( rayPointDistanceSq < localThresholdSq ) { | |
var intersectPoint = ray.closestPointToPoint( point ); | |
intersectPoint.applyMatrix4( matrixWorld ); | |
var distance = raycaster.ray.origin.distanceTo( intersectPoint ); | |
if ( distance < raycaster.near || distance > raycaster.far ) return; | |
intersects.push( { | |
distance: distance, | |
distanceToRay: Math.sqrt( rayPointDistanceSq ), | |
point: intersectPoint.clone(), | |
index: index, | |
face: null, | |
object: object | |
} ); | |
} | |
} | |
if ( geometry instanceof THREE.BufferGeometry ) { | |
var index = geometry.index; | |
var attributes = geometry.attributes; | |
var positions = attributes.position.array; | |
if ( index !== null ) { | |
var indices = index.array; | |
for ( var i = 0, il = indices.length; i < il; i ++ ) { | |
var a = indices[ i ]; | |
position.fromArray( positions, a * 3 ); | |
testPoint( position, a ); | |
} | |
} else { | |
for ( var i = 0, l = positions.length / 3; i < l; i ++ ) { | |
position.fromArray( positions, i * 3 ); | |
testPoint( position, i ); | |
} | |
} | |
} else { | |
var vertices = geometry.vertices; | |
for ( var i = 0, l = vertices.length; i < l; i ++ ) { | |
testPoint( vertices[ i ], i ); | |
} | |
} | |
}; | |
}() ); | |
THREE.Points.prototype.clone = function () { | |
return new this.constructor( this.geometry, this.material ).copy( this ); | |
}; | |
// File:src/objects/Line.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Line = function ( geometry, material, mode ) { | |
if ( mode === 1 ) { | |
console.warn( 'THREE.Line: parameter THREE.LinePieces no longer supported. Created THREE.LineSegments instead.' ); | |
return new THREE.LineSegments( geometry, material ); | |
} | |
THREE.Object3D.call( this ); | |
this.type = 'Line'; | |
this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); | |
this.material = material !== undefined ? material : new THREE.LineBasicMaterial( { color: Math.random() * 0xffffff } ); | |
}; | |
THREE.Line.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Line.prototype.constructor = THREE.Line; | |
THREE.Line.prototype.raycast = ( function () { | |
var inverseMatrix = new THREE.Matrix4(); | |
var ray = new THREE.Ray(); | |
var sphere = new THREE.Sphere(); | |
return function raycast( raycaster, intersects ) { | |
var precision = raycaster.linePrecision; | |
var precisionSq = precision * precision; | |
var geometry = this.geometry; | |
var matrixWorld = this.matrixWorld; | |
// Checking boundingSphere distance to ray | |
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
sphere.copy( geometry.boundingSphere ); | |
sphere.applyMatrix4( matrixWorld ); | |
if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
// | |
inverseMatrix.getInverse( matrixWorld ); | |
ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
var vStart = new THREE.Vector3(); | |
var vEnd = new THREE.Vector3(); | |
var interSegment = new THREE.Vector3(); | |
var interRay = new THREE.Vector3(); | |
var step = this instanceof THREE.LineSegments ? 2 : 1; | |
if ( geometry instanceof THREE.BufferGeometry ) { | |
var index = geometry.index; | |
var attributes = geometry.attributes; | |
var positions = attributes.position.array; | |
if ( index !== null ) { | |
var indices = index.array; | |
for ( var i = 0, l = indices.length - 1; i < l; i += step ) { | |
var a = indices[ i ]; | |
var b = indices[ i + 1 ]; | |
vStart.fromArray( positions, a * 3 ); | |
vEnd.fromArray( positions, b * 3 ); | |
var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); | |
if ( distSq > precisionSq ) continue; | |
interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
var distance = raycaster.ray.origin.distanceTo( interRay ); | |
if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
intersects.push( { | |
distance: distance, | |
// What do we want? intersection point on the ray or on the segment?? | |
// point: raycaster.ray.at( distance ), | |
point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
index: i, | |
face: null, | |
faceIndex: null, | |
object: this | |
} ); | |
} | |
} else { | |
for ( var i = 0, l = positions.length / 3 - 1; i < l; i += step ) { | |
vStart.fromArray( positions, 3 * i ); | |
vEnd.fromArray( positions, 3 * i + 3 ); | |
var distSq = ray.distanceSqToSegment( vStart, vEnd, interRay, interSegment ); | |
if ( distSq > precisionSq ) continue; | |
interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
var distance = raycaster.ray.origin.distanceTo( interRay ); | |
if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
intersects.push( { | |
distance: distance, | |
// What do we want? intersection point on the ray or on the segment?? | |
// point: raycaster.ray.at( distance ), | |
point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
index: i, | |
face: null, | |
faceIndex: null, | |
object: this | |
} ); | |
} | |
} | |
} else if ( geometry instanceof THREE.Geometry ) { | |
var vertices = geometry.vertices; | |
var nbVertices = vertices.length; | |
for ( var i = 0; i < nbVertices - 1; i += step ) { | |
var distSq = ray.distanceSqToSegment( vertices[ i ], vertices[ i + 1 ], interRay, interSegment ); | |
if ( distSq > precisionSq ) continue; | |
interRay.applyMatrix4( this.matrixWorld ); //Move back to world space for distance calculation | |
var distance = raycaster.ray.origin.distanceTo( interRay ); | |
if ( distance < raycaster.near || distance > raycaster.far ) continue; | |
intersects.push( { | |
distance: distance, | |
// What do we want? intersection point on the ray or on the segment?? | |
// point: raycaster.ray.at( distance ), | |
point: interSegment.clone().applyMatrix4( this.matrixWorld ), | |
index: i, | |
face: null, | |
faceIndex: null, | |
object: this | |
} ); | |
} | |
} | |
}; | |
}() ); | |
THREE.Line.prototype.clone = function () { | |
return new this.constructor( this.geometry, this.material ).copy( this ); | |
}; | |
// DEPRECATED | |
THREE.LineStrip = 0; | |
THREE.LinePieces = 1; | |
// File:src/objects/LineSegments.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.LineSegments = function ( geometry, material ) { | |
THREE.Line.call( this, geometry, material ); | |
this.type = 'LineSegments'; | |
}; | |
THREE.LineSegments.prototype = Object.create( THREE.Line.prototype ); | |
THREE.LineSegments.prototype.constructor = THREE.LineSegments; | |
// File:src/objects/Mesh.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
* @author jonobr1 / http://jonobr1.com/ | |
*/ | |
THREE.Mesh = function ( geometry, material ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Mesh'; | |
this.geometry = geometry !== undefined ? geometry : new THREE.Geometry(); | |
this.material = material !== undefined ? material : new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff } ); | |
this.drawMode = THREE.TrianglesDrawMode; | |
this.updateMorphTargets(); | |
}; | |
THREE.Mesh.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Mesh.prototype.constructor = THREE.Mesh; | |
THREE.Mesh.prototype.setDrawMode = function ( value ) { | |
this.drawMode = value; | |
}; | |
THREE.Mesh.prototype.updateMorphTargets = function () { | |
if ( this.geometry.morphTargets !== undefined && this.geometry.morphTargets.length > 0 ) { | |
this.morphTargetBase = - 1; | |
this.morphTargetInfluences = []; | |
this.morphTargetDictionary = {}; | |
for ( var m = 0, ml = this.geometry.morphTargets.length; m < ml; m ++ ) { | |
this.morphTargetInfluences.push( 0 ); | |
this.morphTargetDictionary[ this.geometry.morphTargets[ m ].name ] = m; | |
} | |
} | |
}; | |
THREE.Mesh.prototype.getMorphTargetIndexByName = function ( name ) { | |
if ( this.morphTargetDictionary[ name ] !== undefined ) { | |
return this.morphTargetDictionary[ name ]; | |
} | |
console.warn( 'THREE.Mesh.getMorphTargetIndexByName: morph target ' + name + ' does not exist. Returning 0.' ); | |
return 0; | |
}; | |
THREE.Mesh.prototype.raycast = ( function () { | |
var inverseMatrix = new THREE.Matrix4(); | |
var ray = new THREE.Ray(); | |
var sphere = new THREE.Sphere(); | |
var vA = new THREE.Vector3(); | |
var vB = new THREE.Vector3(); | |
var vC = new THREE.Vector3(); | |
var tempA = new THREE.Vector3(); | |
var tempB = new THREE.Vector3(); | |
var tempC = new THREE.Vector3(); | |
var uvA = new THREE.Vector2(); | |
var uvB = new THREE.Vector2(); | |
var uvC = new THREE.Vector2(); | |
var barycoord = new THREE.Vector3(); | |
var intersectionPoint = new THREE.Vector3(); | |
var intersectionPointWorld = new THREE.Vector3(); | |
function uvIntersection( point, p1, p2, p3, uv1, uv2, uv3 ) { | |
THREE.Triangle.barycoordFromPoint( point, p1, p2, p3, barycoord ); | |
uv1.multiplyScalar( barycoord.x ); | |
uv2.multiplyScalar( barycoord.y ); | |
uv3.multiplyScalar( barycoord.z ); | |
uv1.add( uv2 ).add( uv3 ); | |
return uv1.clone(); | |
} | |
function checkIntersection( object, raycaster, ray, pA, pB, pC, point ) { | |
var intersect; | |
var material = object.material; | |
if ( material.side === THREE.BackSide ) { | |
intersect = ray.intersectTriangle( pC, pB, pA, true, point ); | |
} else { | |
intersect = ray.intersectTriangle( pA, pB, pC, material.side !== THREE.DoubleSide, point ); | |
} | |
if ( intersect === null ) return null; | |
intersectionPointWorld.copy( point ); | |
intersectionPointWorld.applyMatrix4( object.matrixWorld ); | |
var distance = raycaster.ray.origin.distanceTo( intersectionPointWorld ); | |
if ( distance < raycaster.near || distance > raycaster.far ) return null; | |
return { | |
distance: distance, | |
point: intersectionPointWorld.clone(), | |
object: object | |
}; | |
} | |
function checkBufferGeometryIntersection( object, raycaster, ray, positions, uvs, a, b, c ) { | |
vA.fromArray( positions, a * 3 ); | |
vB.fromArray( positions, b * 3 ); | |
vC.fromArray( positions, c * 3 ); | |
var intersection = checkIntersection( object, raycaster, ray, vA, vB, vC, intersectionPoint ); | |
if ( intersection ) { | |
if ( uvs ) { | |
uvA.fromArray( uvs, a * 2 ); | |
uvB.fromArray( uvs, b * 2 ); | |
uvC.fromArray( uvs, c * 2 ); | |
intersection.uv = uvIntersection( intersectionPoint, vA, vB, vC, uvA, uvB, uvC ); | |
} | |
intersection.face = new THREE.Face3( a, b, c, THREE.Triangle.normal( vA, vB, vC ) ); | |
intersection.faceIndex = a; | |
} | |
return intersection; | |
} | |
return function raycast( raycaster, intersects ) { | |
var geometry = this.geometry; | |
var material = this.material; | |
var matrixWorld = this.matrixWorld; | |
if ( material === undefined ) return; | |
// Checking boundingSphere distance to ray | |
if ( geometry.boundingSphere === null ) geometry.computeBoundingSphere(); | |
sphere.copy( geometry.boundingSphere ); | |
sphere.applyMatrix4( matrixWorld ); | |
if ( raycaster.ray.intersectsSphere( sphere ) === false ) return; | |
// | |
inverseMatrix.getInverse( matrixWorld ); | |
ray.copy( raycaster.ray ).applyMatrix4( inverseMatrix ); | |
// Check boundingBox before continuing | |
if ( geometry.boundingBox !== null ) { | |
if ( ray.intersectsBox( geometry.boundingBox ) === false ) return; | |
} | |
var uvs, intersection; | |
if ( geometry instanceof THREE.BufferGeometry ) { | |
var a, b, c; | |
var index = geometry.index; | |
var attributes = geometry.attributes; | |
var positions = attributes.position.array; | |
if ( attributes.uv !== undefined ) { | |
uvs = attributes.uv.array; | |
} | |
if ( index !== null ) { | |
var indices = index.array; | |
for ( var i = 0, l = indices.length; i < l; i += 3 ) { | |
a = indices[ i ]; | |
b = indices[ i + 1 ]; | |
c = indices[ i + 2 ]; | |
intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); | |
if ( intersection ) { | |
intersection.faceIndex = Math.floor( i / 3 ); // triangle number in indices buffer semantics | |
intersects.push( intersection ); | |
} | |
} | |
} else { | |
for ( var i = 0, l = positions.length; i < l; i += 9 ) { | |
a = i / 3; | |
b = a + 1; | |
c = a + 2; | |
intersection = checkBufferGeometryIntersection( this, raycaster, ray, positions, uvs, a, b, c ); | |
if ( intersection ) { | |
intersection.index = a; // triangle number in positions buffer semantics | |
intersects.push( intersection ); | |
} | |
} | |
} | |
} else if ( geometry instanceof THREE.Geometry ) { | |
var fvA, fvB, fvC; | |
var isFaceMaterial = material instanceof THREE.MultiMaterial; | |
var materials = isFaceMaterial === true ? material.materials : null; | |
var vertices = geometry.vertices; | |
var faces = geometry.faces; | |
var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; | |
if ( faceVertexUvs.length > 0 ) uvs = faceVertexUvs; | |
for ( var f = 0, fl = faces.length; f < fl; f ++ ) { | |
var face = faces[ f ]; | |
var faceMaterial = isFaceMaterial === true ? materials[ face.materialIndex ] : material; | |
if ( faceMaterial === undefined ) continue; | |
fvA = vertices[ face.a ]; | |
fvB = vertices[ face.b ]; | |
fvC = vertices[ face.c ]; | |
if ( faceMaterial.morphTargets === true ) { | |
var morphTargets = geometry.morphTargets; | |
var morphInfluences = this.morphTargetInfluences; | |
vA.set( 0, 0, 0 ); | |
vB.set( 0, 0, 0 ); | |
vC.set( 0, 0, 0 ); | |
for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { | |
var influence = morphInfluences[ t ]; | |
if ( influence === 0 ) continue; | |
var targets = morphTargets[ t ].vertices; | |
vA.addScaledVector( tempA.subVectors( targets[ face.a ], fvA ), influence ); | |
vB.addScaledVector( tempB.subVectors( targets[ face.b ], fvB ), influence ); | |
vC.addScaledVector( tempC.subVectors( targets[ face.c ], fvC ), influence ); | |
} | |
vA.add( fvA ); | |
vB.add( fvB ); | |
vC.add( fvC ); | |
fvA = vA; | |
fvB = vB; | |
fvC = vC; | |
} | |
intersection = checkIntersection( this, raycaster, ray, fvA, fvB, fvC, intersectionPoint ); | |
if ( intersection ) { | |
if ( uvs ) { | |
var uvs_f = uvs[ f ]; | |
uvA.copy( uvs_f[ 0 ] ); | |
uvB.copy( uvs_f[ 1 ] ); | |
uvC.copy( uvs_f[ 2 ] ); | |
intersection.uv = uvIntersection( intersectionPoint, fvA, fvB, fvC, uvA, uvB, uvC ); | |
} | |
intersection.face = face; | |
intersection.faceIndex = f; | |
intersects.push( intersection ); | |
} | |
} | |
} | |
}; | |
}() ); | |
THREE.Mesh.prototype.clone = function () { | |
return new this.constructor( this.geometry, this.material ).copy( this ); | |
}; | |
// File:src/objects/Bone.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author ikerr / http://verold.com | |
*/ | |
THREE.Bone = function ( skin ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Bone'; | |
this.skin = skin; | |
}; | |
THREE.Bone.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Bone.prototype.constructor = THREE.Bone; | |
THREE.Bone.prototype.copy = function ( source ) { | |
THREE.Object3D.prototype.copy.call( this, source ); | |
this.skin = source.skin; | |
return this; | |
}; | |
// File:src/objects/Skeleton.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author michael guerrero / http://realitymeltdown.com | |
* @author ikerr / http://verold.com | |
*/ | |
THREE.Skeleton = function ( bones, boneInverses, useVertexTexture ) { | |
this.useVertexTexture = useVertexTexture !== undefined ? useVertexTexture : true; | |
this.identityMatrix = new THREE.Matrix4(); | |
// copy the bone array | |
bones = bones || []; | |
this.bones = bones.slice( 0 ); | |
// create a bone texture or an array of floats | |
if ( this.useVertexTexture ) { | |
// layout (1 matrix = 4 pixels) | |
// RGBA RGBA RGBA RGBA (=> column1, column2, column3, column4) | |
// with 8x8 pixel texture max 16 bones * 4 pixels = (8 * 8) | |
// 16x16 pixel texture max 64 bones * 4 pixels = (16 * 16) | |
// 32x32 pixel texture max 256 bones * 4 pixels = (32 * 32) | |
// 64x64 pixel texture max 1024 bones * 4 pixels = (64 * 64) | |
var size = Math.sqrt( this.bones.length * 4 ); // 4 pixels needed for 1 matrix | |
size = THREE.Math.nextPowerOfTwo( Math.ceil( size ) ); | |
size = Math.max( size, 4 ); | |
this.boneTextureWidth = size; | |
this.boneTextureHeight = size; | |
this.boneMatrices = new Float32Array( this.boneTextureWidth * this.boneTextureHeight * 4 ); // 4 floats per RGBA pixel | |
this.boneTexture = new THREE.DataTexture( this.boneMatrices, this.boneTextureWidth, this.boneTextureHeight, THREE.RGBAFormat, THREE.FloatType ); | |
} else { | |
this.boneMatrices = new Float32Array( 16 * this.bones.length ); | |
} | |
// use the supplied bone inverses or calculate the inverses | |
if ( boneInverses === undefined ) { | |
this.calculateInverses(); | |
} else { | |
if ( this.bones.length === boneInverses.length ) { | |
this.boneInverses = boneInverses.slice( 0 ); | |
} else { | |
console.warn( 'THREE.Skeleton bonInverses is the wrong length.' ); | |
this.boneInverses = []; | |
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { | |
this.boneInverses.push( new THREE.Matrix4() ); | |
} | |
} | |
} | |
}; | |
THREE.Skeleton.prototype.calculateInverses = function () { | |
this.boneInverses = []; | |
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { | |
var inverse = new THREE.Matrix4(); | |
if ( this.bones[ b ] ) { | |
inverse.getInverse( this.bones[ b ].matrixWorld ); | |
} | |
this.boneInverses.push( inverse ); | |
} | |
}; | |
THREE.Skeleton.prototype.pose = function () { | |
var bone; | |
// recover the bind-time world matrices | |
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { | |
bone = this.bones[ b ]; | |
if ( bone ) { | |
bone.matrixWorld.getInverse( this.boneInverses[ b ] ); | |
} | |
} | |
// compute the local matrices, positions, rotations and scales | |
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { | |
bone = this.bones[ b ]; | |
if ( bone ) { | |
if ( bone.parent ) { | |
bone.matrix.getInverse( bone.parent.matrixWorld ); | |
bone.matrix.multiply( bone.matrixWorld ); | |
} else { | |
bone.matrix.copy( bone.matrixWorld ); | |
} | |
bone.matrix.decompose( bone.position, bone.quaternion, bone.scale ); | |
} | |
} | |
}; | |
THREE.Skeleton.prototype.update = ( function () { | |
var offsetMatrix = new THREE.Matrix4(); | |
return function update() { | |
// flatten bone matrices to array | |
for ( var b = 0, bl = this.bones.length; b < bl; b ++ ) { | |
// compute the offset between the current and the original transform | |
var matrix = this.bones[ b ] ? this.bones[ b ].matrixWorld : this.identityMatrix; | |
offsetMatrix.multiplyMatrices( matrix, this.boneInverses[ b ] ); | |
offsetMatrix.flattenToArrayOffset( this.boneMatrices, b * 16 ); | |
} | |
if ( this.useVertexTexture ) { | |
this.boneTexture.needsUpdate = true; | |
} | |
}; | |
} )(); | |
THREE.Skeleton.prototype.clone = function () { | |
return new THREE.Skeleton( this.bones, this.boneInverses, this.useVertexTexture ); | |
}; | |
// File:src/objects/SkinnedMesh.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author ikerr / http://verold.com | |
*/ | |
THREE.SkinnedMesh = function ( geometry, material, useVertexTexture ) { | |
THREE.Mesh.call( this, geometry, material ); | |
this.type = 'SkinnedMesh'; | |
this.bindMode = "attached"; | |
this.bindMatrix = new THREE.Matrix4(); | |
this.bindMatrixInverse = new THREE.Matrix4(); | |
// init bones | |
// TODO: remove bone creation as there is no reason (other than | |
// convenience) for THREE.SkinnedMesh to do this. | |
var bones = []; | |
if ( this.geometry && this.geometry.bones !== undefined ) { | |
var bone, gbone; | |
for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { | |
gbone = this.geometry.bones[ b ]; | |
bone = new THREE.Bone( this ); | |
bones.push( bone ); | |
bone.name = gbone.name; | |
bone.position.fromArray( gbone.pos ); | |
bone.quaternion.fromArray( gbone.rotq ); | |
if ( gbone.scl !== undefined ) bone.scale.fromArray( gbone.scl ); | |
} | |
for ( var b = 0, bl = this.geometry.bones.length; b < bl; ++ b ) { | |
gbone = this.geometry.bones[ b ]; | |
if ( gbone.parent !== - 1 && gbone.parent !== null ) { | |
bones[ gbone.parent ].add( bones[ b ] ); | |
} else { | |
this.add( bones[ b ] ); | |
} | |
} | |
} | |
this.normalizeSkinWeights(); | |
this.updateMatrixWorld( true ); | |
this.bind( new THREE.Skeleton( bones, undefined, useVertexTexture ), this.matrixWorld ); | |
}; | |
THREE.SkinnedMesh.prototype = Object.create( THREE.Mesh.prototype ); | |
THREE.SkinnedMesh.prototype.constructor = THREE.SkinnedMesh; | |
THREE.SkinnedMesh.prototype.bind = function( skeleton, bindMatrix ) { | |
this.skeleton = skeleton; | |
if ( bindMatrix === undefined ) { | |
this.updateMatrixWorld( true ); | |
this.skeleton.calculateInverses(); | |
bindMatrix = this.matrixWorld; | |
} | |
this.bindMatrix.copy( bindMatrix ); | |
this.bindMatrixInverse.getInverse( bindMatrix ); | |
}; | |
THREE.SkinnedMesh.prototype.pose = function () { | |
this.skeleton.pose(); | |
}; | |
THREE.SkinnedMesh.prototype.normalizeSkinWeights = function () { | |
if ( this.geometry instanceof THREE.Geometry ) { | |
for ( var i = 0; i < this.geometry.skinWeights.length; i ++ ) { | |
var sw = this.geometry.skinWeights[ i ]; | |
var scale = 1.0 / sw.lengthManhattan(); | |
if ( scale !== Infinity ) { | |
sw.multiplyScalar( scale ); | |
} else { | |
sw.set( 1, 0, 0, 0 ); // do something reasonable | |
} | |
} | |
} else if ( this.geometry instanceof THREE.BufferGeometry ) { | |
var vec = new THREE.Vector4(); | |
var skinWeight = this.geometry.attributes.skinWeight; | |
for ( var i = 0; i < skinWeight.count; i ++ ) { | |
vec.x = skinWeight.getX( i ); | |
vec.y = skinWeight.getY( i ); | |
vec.z = skinWeight.getZ( i ); | |
vec.w = skinWeight.getW( i ); | |
var scale = 1.0 / vec.lengthManhattan(); | |
if ( scale !== Infinity ) { | |
vec.multiplyScalar( scale ); | |
} else { | |
vec.set( 1, 0, 0, 0 ); // do something reasonable | |
} | |
skinWeight.setXYZW( i, vec.x, vec.y, vec.z, vec.w ); | |
} | |
} | |
}; | |
THREE.SkinnedMesh.prototype.updateMatrixWorld = function( force ) { | |
THREE.Mesh.prototype.updateMatrixWorld.call( this, true ); | |
if ( this.bindMode === "attached" ) { | |
this.bindMatrixInverse.getInverse( this.matrixWorld ); | |
} else if ( this.bindMode === "detached" ) { | |
this.bindMatrixInverse.getInverse( this.bindMatrix ); | |
} else { | |
console.warn( 'THREE.SkinnedMesh unrecognized bindMode: ' + this.bindMode ); | |
} | |
}; | |
THREE.SkinnedMesh.prototype.clone = function() { | |
return new this.constructor( this.geometry, this.material, this.useVertexTexture ).copy( this ); | |
}; | |
// File:src/objects/LOD.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.LOD = function () { | |
THREE.Object3D.call( this ); | |
this.type = 'LOD'; | |
Object.defineProperties( this, { | |
levels: { | |
enumerable: true, | |
value: [] | |
}, | |
objects: { | |
get: function () { | |
console.warn( 'THREE.LOD: .objects has been renamed to .levels.' ); | |
return this.levels; | |
} | |
} | |
} ); | |
}; | |
THREE.LOD.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.LOD.prototype.constructor = THREE.LOD; | |
THREE.LOD.prototype.addLevel = function ( object, distance ) { | |
if ( distance === undefined ) distance = 0; | |
distance = Math.abs( distance ); | |
var levels = this.levels; | |
for ( var l = 0; l < levels.length; l ++ ) { | |
if ( distance < levels[ l ].distance ) { | |
break; | |
} | |
} | |
levels.splice( l, 0, { distance: distance, object: object } ); | |
this.add( object ); | |
}; | |
THREE.LOD.prototype.getObjectForDistance = function ( distance ) { | |
var levels = this.levels; | |
for ( var i = 1, l = levels.length; i < l; i ++ ) { | |
if ( distance < levels[ i ].distance ) { | |
break; | |
} | |
} | |
return levels[ i - 1 ].object; | |
}; | |
THREE.LOD.prototype.raycast = ( function () { | |
var matrixPosition = new THREE.Vector3(); | |
return function raycast( raycaster, intersects ) { | |
matrixPosition.setFromMatrixPosition( this.matrixWorld ); | |
var distance = raycaster.ray.origin.distanceTo( matrixPosition ); | |
this.getObjectForDistance( distance ).raycast( raycaster, intersects ); | |
}; | |
}() ); | |
THREE.LOD.prototype.update = function () { | |
var v1 = new THREE.Vector3(); | |
var v2 = new THREE.Vector3(); | |
return function update( camera ) { | |
var levels = this.levels; | |
if ( levels.length > 1 ) { | |
v1.setFromMatrixPosition( camera.matrixWorld ); | |
v2.setFromMatrixPosition( this.matrixWorld ); | |
var distance = v1.distanceTo( v2 ); | |
levels[ 0 ].object.visible = true; | |
for ( var i = 1, l = levels.length; i < l; i ++ ) { | |
if ( distance >= levels[ i ].distance ) { | |
levels[ i - 1 ].object.visible = false; | |
levels[ i ].object.visible = true; | |
} else { | |
break; | |
} | |
} | |
for ( ; i < l; i ++ ) { | |
levels[ i ].object.visible = false; | |
} | |
} | |
}; | |
}(); | |
THREE.LOD.prototype.copy = function ( source ) { | |
THREE.Object3D.prototype.copy.call( this, source, false ); | |
var levels = source.levels; | |
for ( var i = 0, l = levels.length; i < l; i ++ ) { | |
var level = levels[ i ]; | |
this.addLevel( level.object.clone(), level.distance ); | |
} | |
return this; | |
}; | |
THREE.LOD.prototype.toJSON = function ( meta ) { | |
var data = THREE.Object3D.prototype.toJSON.call( this, meta ); | |
data.object.levels = []; | |
var levels = this.levels; | |
for ( var i = 0, l = levels.length; i < l; i ++ ) { | |
var level = levels[ i ]; | |
data.object.levels.push( { | |
object: level.object.uuid, | |
distance: level.distance | |
} ); | |
} | |
return data; | |
}; | |
// File:src/objects/Sprite.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Sprite = ( function () { | |
var indices = new Uint16Array( [ 0, 1, 2, 0, 2, 3 ] ); | |
var vertices = new Float32Array( [ - 0.5, - 0.5, 0, 0.5, - 0.5, 0, 0.5, 0.5, 0, - 0.5, 0.5, 0 ] ); | |
var uvs = new Float32Array( [ 0, 0, 1, 0, 1, 1, 0, 1 ] ); | |
var geometry = new THREE.BufferGeometry(); | |
geometry.setIndex( new THREE.BufferAttribute( indices, 1 ) ); | |
geometry.addAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) ); | |
geometry.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); | |
return function Sprite( material ) { | |
THREE.Object3D.call( this ); | |
this.type = 'Sprite'; | |
this.geometry = geometry; | |
this.material = ( material !== undefined ) ? material : new THREE.SpriteMaterial(); | |
}; | |
} )(); | |
THREE.Sprite.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Sprite.prototype.constructor = THREE.Sprite; | |
THREE.Sprite.prototype.raycast = ( function () { | |
var matrixPosition = new THREE.Vector3(); | |
return function raycast( raycaster, intersects ) { | |
matrixPosition.setFromMatrixPosition( this.matrixWorld ); | |
var distanceSq = raycaster.ray.distanceSqToPoint( matrixPosition ); | |
var guessSizeSq = this.scale.x * this.scale.y; | |
if ( distanceSq > guessSizeSq ) { | |
return; | |
} | |
intersects.push( { | |
distance: Math.sqrt( distanceSq ), | |
point: this.position, | |
face: null, | |
object: this | |
} ); | |
}; | |
}() ); | |
THREE.Sprite.prototype.clone = function () { | |
return new this.constructor( this.material ).copy( this ); | |
}; | |
// Backwards compatibility | |
THREE.Particle = THREE.Sprite; | |
// File:src/objects/LensFlare.js | |
/** | |
* @author mikael emtinger / http://gomo.se/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.LensFlare = function ( texture, size, distance, blending, color ) { | |
THREE.Object3D.call( this ); | |
this.lensFlares = []; | |
this.positionScreen = new THREE.Vector3(); | |
this.customUpdateCallback = undefined; | |
if ( texture !== undefined ) { | |
this.add( texture, size, distance, blending, color ); | |
} | |
}; | |
THREE.LensFlare.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.LensFlare.prototype.constructor = THREE.LensFlare; | |
/* | |
* Add: adds another flare | |
*/ | |
THREE.LensFlare.prototype.add = function ( texture, size, distance, blending, color, opacity ) { | |
if ( size === undefined ) size = - 1; | |
if ( distance === undefined ) distance = 0; | |
if ( opacity === undefined ) opacity = 1; | |
if ( color === undefined ) color = new THREE.Color( 0xffffff ); | |
if ( blending === undefined ) blending = THREE.NormalBlending; | |
distance = Math.min( distance, Math.max( 0, distance ) ); | |
this.lensFlares.push( { | |
texture: texture, // THREE.Texture | |
size: size, // size in pixels (-1 = use texture.width) | |
distance: distance, // distance (0-1) from light source (0=at light source) | |
x: 0, y: 0, z: 0, // screen position (-1 => 1) z = 0 is in front z = 1 is back | |
scale: 1, // scale | |
rotation: 0, // rotation | |
opacity: opacity, // opacity | |
color: color, // color | |
blending: blending // blending | |
} ); | |
}; | |
/* | |
* Update lens flares update positions on all flares based on the screen position | |
* Set myLensFlare.customUpdateCallback to alter the flares in your project specific way. | |
*/ | |
THREE.LensFlare.prototype.updateLensFlares = function () { | |
var f, fl = this.lensFlares.length; | |
var flare; | |
var vecX = - this.positionScreen.x * 2; | |
var vecY = - this.positionScreen.y * 2; | |
for ( f = 0; f < fl; f ++ ) { | |
flare = this.lensFlares[ f ]; | |
flare.x = this.positionScreen.x + vecX * flare.distance; | |
flare.y = this.positionScreen.y + vecY * flare.distance; | |
flare.wantedRotation = flare.x * Math.PI * 0.25; | |
flare.rotation += ( flare.wantedRotation - flare.rotation ) * 0.25; | |
} | |
}; | |
THREE.LensFlare.prototype.copy = function ( source ) { | |
THREE.Object3D.prototype.copy.call( this, source ); | |
this.positionScreen.copy( source.positionScreen ); | |
this.customUpdateCallback = source.customUpdateCallback; | |
for ( var i = 0, l = source.lensFlares.length; i < l; i ++ ) { | |
this.lensFlares.push( source.lensFlares[ i ] ); | |
} | |
return this; | |
}; | |
// File:src/scenes/Scene.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
*/ | |
THREE.Scene = function () { | |
THREE.Object3D.call( this ); | |
this.type = 'Scene'; | |
this.fog = null; | |
this.overrideMaterial = null; | |
this.autoUpdate = true; // checked by the renderer | |
}; | |
THREE.Scene.prototype = Object.create( THREE.Object3D.prototype ); | |
THREE.Scene.prototype.constructor = THREE.Scene; | |
THREE.Scene.prototype.copy = function ( source, recursive ) { | |
THREE.Object3D.prototype.copy.call( this, source, recursive ); | |
if ( source.fog !== null ) this.fog = source.fog.clone(); | |
if ( source.overrideMaterial !== null ) this.overrideMaterial = source.overrideMaterial.clone(); | |
this.autoUpdate = source.autoUpdate; | |
this.matrixAutoUpdate = source.matrixAutoUpdate; | |
return this; | |
}; | |
// File:src/scenes/Fog.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.Fog = function ( color, near, far ) { | |
this.name = ''; | |
this.color = new THREE.Color( color ); | |
this.near = ( near !== undefined ) ? near : 1; | |
this.far = ( far !== undefined ) ? far : 1000; | |
}; | |
THREE.Fog.prototype.clone = function () { | |
return new THREE.Fog( this.color.getHex(), this.near, this.far ); | |
}; | |
// File:src/scenes/FogExp2.js | |
/** | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
*/ | |
THREE.FogExp2 = function ( color, density ) { | |
this.name = ''; | |
this.color = new THREE.Color( color ); | |
this.density = ( density !== undefined ) ? density : 0.00025; | |
}; | |
THREE.FogExp2.prototype.clone = function () { | |
return new THREE.FogExp2( this.color.getHex(), this.density ); | |
}; | |
// File:src/renderers/shaders/ShaderChunk.js | |
THREE.ShaderChunk = {}; | |
// File:src/renderers/shaders/ShaderChunk/alphamap_fragment.glsl | |
THREE.ShaderChunk[ 'alphamap_fragment' ] = "#ifdef USE_ALPHAMAP\n diffuseColor.a *= texture2D( alphaMap, vUv ).g;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/alphamap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'alphamap_pars_fragment' ] = "#ifdef USE_ALPHAMAP\n uniform sampler2D alphaMap;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/alphatest_fragment.glsl | |
THREE.ShaderChunk[ 'alphatest_fragment' ] = "#ifdef ALPHATEST\n if ( diffuseColor.a < ALPHATEST ) discard;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/ambient_pars.glsl | |
THREE.ShaderChunk[ 'ambient_pars' ] = "uniform vec3 ambientLightColor;\nvec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) {\n return PI * ambientLightColor;\n}\n"; | |
// File:src/renderers/shaders/ShaderChunk/aomap_fragment.glsl | |
THREE.ShaderChunk[ 'aomap_fragment' ] = "#ifdef USE_AOMAP\n reflectedLight.indirectDiffuse *= ( texture2D( aoMap, vUv2 ).r - 1.0 ) * aoMapIntensity + 1.0;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/aomap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'aomap_pars_fragment' ] = "#ifdef USE_AOMAP\n uniform sampler2D aoMap;\n uniform float aoMapIntensity;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/begin_vertex.glsl | |
THREE.ShaderChunk[ 'begin_vertex' ] = "\nvec3 transformed = vec3( position );\n"; | |
// File:src/renderers/shaders/ShaderChunk/beginnormal_vertex.glsl | |
THREE.ShaderChunk[ 'beginnormal_vertex' ] = "\nvec3 objectNormal = vec3( normal );\n"; | |
// File:src/renderers/shaders/ShaderChunk/bsdfs.glsl | |
THREE.ShaderChunk[ 'bsdfs' ] = "bool testLightInRange( const in float lightDistance, const in float cutoffDistance ) {\n return any( bvec2( cutoffDistance == 0.0, lightDistance < cutoffDistance ) );\n}\nfloat calcLightAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {\n if ( decayExponent > 0.0 ) {\n return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );\n }\n return 1.0;\n}\nvec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {\n return RECIPROCAL_PI * diffuseColor;\n}\nvec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {\n float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );\n return ( 1.0 - specularColor ) * fresnel + specularColor;\n}\nfloat G_GGX_Smith( const in float alpha, const in float dotNL, const in float dotNV ) {\n float a2 = alpha * alpha;\n float gl = dotNL + pow( a2 + ( 1.0 - a2 ) * dotNL * dotNL, 0.5 );\n float gv = dotNV + pow( a2 + ( 1.0 - a2 ) * dotNV * dotNV, 0.5 );\n return 1.0 / ( gl * gv );\n}\nfloat D_GGX( const in float alpha, const in float dotNH ) {\n float a2 = alpha * alpha;\n float denom = dotNH * dotNH * ( a2 - 1.0 ) + 1.0;\n return RECIPROCAL_PI * a2 / ( denom * denom );\n}\nvec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float alpha = roughness * roughness;\n vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNL = saturate( dot( geometry.normal, incidentLight.direction ) );\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_GGX_Smith( alpha, dotNL, dotNV );\n float D = D_GGX( alpha, dotNH );\n return F * ( G * D );\n}\nvec3 BRDF_Specular_GGX_Environment( const in GeometricContext geometry, const in vec3 specularColor, const in float roughness ) {\n float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );\n const vec4 c0 = vec4( - 1, - 0.0275, - 0.572, 0.022 );\n const vec4 c1 = vec4( 1, 0.0425, 1.04, - 0.04 );\n vec4 r = roughness * c0 + c1;\n float a004 = min( r.x * r.x, exp2( - 9.28 * dotNV ) ) * r.x + r.y;\n vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;\n return specularColor * AB.x + AB.y;\n}\nfloat G_BlinnPhong_Implicit( ) {\n return 0.25;\n}\nfloat D_BlinnPhong( const in float shininess, const in float dotNH ) {\n return 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 vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );\n float dotNH = saturate( dot( geometry.normal, halfDir ) );\n float dotLH = saturate( dot( incidentLight.direction, halfDir ) );\n vec3 F = F_Schlick( specularColor, dotLH );\n float G = G_BlinnPhong_Implicit( );\n float D = D_BlinnPhong( shininess, dotNH );\n return F * ( G * D );\n}\nfloat GGXRoughnessToBlinnExponent( const in float ggxRoughness ) {\n return ( 2.0 / square( ggxRoughness + 0.0001 ) - 2.0 );\n}\n"; | |
// File:src/renderers/shaders/ShaderChunk/bumpmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'bumpmap_pars_fragment' ] = "#ifdef USE_BUMPMAP\n uniform sampler2D bumpMap;\n uniform float bumpScale;\n vec2 dHdxy_fwd() {\n vec2 dSTdx = dFdx( vUv );\n vec2 dSTdy = dFdy( vUv );\n float Hll = bumpScale * texture2D( bumpMap, vUv ).x;\n float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;\n float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;\n return vec2( dBx, dBy );\n }\n vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {\n vec3 vSigmaX = dFdx( surf_pos );\n vec3 vSigmaY = dFdy( surf_pos );\n vec3 vN = surf_norm;\n vec3 R1 = cross( vSigmaY, vN );\n vec3 R2 = cross( vN, vSigmaX );\n float fDet = dot( vSigmaX, R1 );\n vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );\n return normalize( abs( fDet ) * surf_norm - vGrad );\n }\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/color_fragment.glsl | |
THREE.ShaderChunk[ 'color_fragment' ] = "#ifdef USE_COLOR\n diffuseColor.rgb *= vColor;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/color_pars_fragment.glsl | |
THREE.ShaderChunk[ 'color_pars_fragment' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/color_pars_vertex.glsl | |
THREE.ShaderChunk[ 'color_pars_vertex' ] = "#ifdef USE_COLOR\n varying vec3 vColor;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/color_vertex.glsl | |
THREE.ShaderChunk[ 'color_vertex' ] = "#ifdef USE_COLOR\n vColor.xyz = color.xyz;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/common.glsl | |
THREE.ShaderChunk[ 'common' ] = "#define PI 3.14159\n#define PI2 6.28318\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 square( const in float x ) { return x*x; }\nfloat average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }\nstruct IncidentLight {\n vec3 color;\n vec3 direction;\n bool visible;\n};\nstruct ReflectedLight {\n vec3 directDiffuse;\n vec3 directSpecular;\n vec3 indirectDiffuse;\n vec3 indirectSpecular;\n};\nstruct GeometricContext {\n vec3 position;\n vec3 normal;\n vec3 viewDir;\n};\nvec3 transformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );\n}\nvec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {\n return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );\n}\nvec3 projectOnPlane(in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n float distance = dot( planeNormal, point - pointOnPlane );\n return - distance * planeNormal + point;\n}\nfloat sideOfPlane( in vec3 point, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return sign( dot( point - pointOnPlane, planeNormal ) );\n}\nvec3 linePlaneIntersect( in vec3 pointOnLine, in vec3 lineDirection, in vec3 pointOnPlane, in vec3 planeNormal ) {\n return lineDirection * ( dot( planeNormal, pointOnPlane - pointOnLine ) / dot( planeNormal, lineDirection ) ) + pointOnLine;\n}\nvec3 inputToLinear( in vec3 a ) {\n #ifdef GAMMA_INPUT\n return pow( a, vec3( float( GAMMA_FACTOR ) ) );\n #else\n return a;\n #endif\n}\nvec3 linearToOutput( in vec3 a ) {\n #ifdef GAMMA_OUTPUT\n return pow( a, vec3( 1.0 / float( GAMMA_FACTOR ) ) );\n #else\n return a;\n #endif\n}\n"; | |
// File:src/renderers/shaders/ShaderChunk/defaultnormal_vertex.glsl | |
THREE.ShaderChunk[ 'defaultnormal_vertex' ] = "#ifdef FLIP_SIDED\n objectNormal = -objectNormal;\n#endif\nvec3 transformedNormal = normalMatrix * objectNormal;\n"; | |
// File:src/renderers/shaders/ShaderChunk/displacementmap_vertex.glsl | |
THREE.ShaderChunk[ 'displacementmap_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n transformed += normal * ( texture2D( displacementMap, uv ).x * displacementScale + displacementBias );\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/displacementmap_pars_vertex.glsl | |
THREE.ShaderChunk[ 'displacementmap_pars_vertex' ] = "#ifdef USE_DISPLACEMENTMAP\n uniform sampler2D displacementMap;\n uniform float displacementScale;\n uniform float displacementBias;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/emissivemap_fragment.glsl | |
THREE.ShaderChunk[ 'emissivemap_fragment' ] = "#ifdef USE_EMISSIVEMAP\n vec4 emissiveColor = texture2D( emissiveMap, vUv );\n emissiveColor.rgb = inputToLinear( emissiveColor.rgb );\n totalEmissiveLight *= emissiveColor.rgb;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/emissivemap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'emissivemap_pars_fragment' ] = "#ifdef USE_EMISSIVEMAP\n uniform sampler2D emissiveMap;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/envmap_fragment.glsl | |
THREE.ShaderChunk[ 'envmap_fragment' ] = "#ifdef USE_ENVMAP\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG )\n vec3 cameraToVertex = normalize( vWorldPosition - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( cameraToVertex, worldNormal );\n #else\n vec3 reflectVec = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n #else\n vec3 reflectVec = vReflect;\n #endif\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n #ifdef ENVMAP_TYPE_CUBE\n vec4 envColor = textureCube( envMap, flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) );\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n vec4 envColor = texture2D( envMap, sampleUV );\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n vec4 envColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5 );\n #endif\n envColor.xyz = inputToLinear( envColor.xyz );\n #ifdef ENVMAP_BLENDING_MULTIPLY\n outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_MIX )\n outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity );\n #elif defined( ENVMAP_BLENDING_ADD )\n outgoingLight += envColor.xyz * specularStrength * reflectivity;\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/envmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'envmap_pars_fragment' ] = "#if defined( USE_ENVMAP ) || defined( STANDARD )\n uniform float reflectivity;\n uniform float envMapIntenstiy;\n#endif\n#ifdef USE_ENVMAP\n #ifdef ENVMAP_TYPE_CUBE\n uniform samplerCube envMap;\n #else\n uniform sampler2D envMap;\n #endif\n uniform float flipEnvMap;\n #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( STANDARD )\n uniform float refractionRatio;\n #else\n varying vec3 vReflect;\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/envmap_pars_vertex.glsl | |
THREE.ShaderChunk[ 'envmap_pars_vertex' ] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )\n varying vec3 vReflect;\n uniform float refractionRatio;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/envmap_vertex.glsl | |
THREE.ShaderChunk[ 'envmap_vertex' ] = "#if defined( USE_ENVMAP ) && ! defined( USE_BUMPMAP ) && ! defined( USE_NORMALMAP ) && ! defined( PHONG ) && ! defined( STANDARD )\n vec3 cameraToVertex = normalize( worldPosition.xyz - cameraPosition );\n vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix );\n #ifdef ENVMAP_MODE_REFLECTION\n vReflect = reflect( cameraToVertex, worldNormal );\n #else\n vReflect = refract( cameraToVertex, worldNormal, refractionRatio );\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/fog_fragment.glsl | |
THREE.ShaderChunk[ 'fog_fragment' ] = "#ifdef USE_FOG\n #ifdef USE_LOGDEPTHBUF_EXT\n float depth = gl_FragDepthEXT / gl_FragCoord.w;\n #else\n float depth = gl_FragCoord.z / gl_FragCoord.w;\n #endif\n #ifdef FOG_EXP2\n float fogFactor = whiteCompliment( exp2( - fogDensity * fogDensity * depth * depth * LOG2 ) );\n #else\n float fogFactor = smoothstep( fogNear, fogFar, depth );\n #endif\n \n outgoingLight = mix( outgoingLight, fogColor, fogFactor );\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/fog_pars_fragment.glsl | |
THREE.ShaderChunk[ 'fog_pars_fragment' ] = "#ifdef USE_FOG\n uniform vec3 fogColor;\n #ifdef FOG_EXP2\n uniform float fogDensity;\n #else\n uniform float fogNear;\n uniform float fogFar;\n #endif\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/lightmap_fragment.glsl | |
THREE.ShaderChunk[ 'lightmap_fragment' ] = "#ifdef USE_LIGHTMAP\n reflectedLight.indirectDiffuse += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/lightmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'lightmap_pars_fragment' ] = "#ifdef USE_LIGHTMAP\n uniform sampler2D lightMap;\n uniform float lightMapIntensity;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/lights_lambert_vertex.glsl | |
THREE.ShaderChunk[ '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 vLightBack = vec3( 0.0 );\n#endif\nIncidentLight directLight;\nfloat dotNL;\nvec3 directLightColor_Diffuse;\n#if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n directLight = getPointDirectLight( pointLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n directLight = getSpotDirectLight( spotLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directLight = getDirectionalDirectLight( directionalLights[ i ], geometry );\n dotNL = dot( geometry.normal, directLight.direction );\n directLightColor_Diffuse = PI * directLight.color;\n vLightFront += saturate( dotNL ) * directLightColor_Diffuse;\n #ifdef DOUBLE_SIDED\n vLightBack += saturate( -dotNL ) * directLightColor_Diffuse;\n #endif\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n vLightFront += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n #ifdef DOUBLE_SIDED\n vLightBack += getHemisphereLightIrradiance( hemisphereLights[ i ], backGeometry );\n #endif\n }\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_pars.glsl | |
THREE.ShaderChunk[ 'lights_pars' ] = "#if NUM_DIR_LIGHTS > 0\n struct DirectionalLight {\n vec3 direction;\n vec3 color;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];\n IncidentLight getDirectionalDirectLight( const in DirectionalLight directionalLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n directLight.color = directionalLight.color;\n directLight.direction = directionalLight.direction;\n directLight.visible = true;\n return directLight;\n }\n#endif\n#if NUM_POINT_LIGHTS > 0\n struct PointLight {\n vec3 position;\n vec3 color;\n float distance;\n float decay;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform PointLight pointLights[ NUM_POINT_LIGHTS ];\n IncidentLight getPointDirectLight( const in PointLight pointLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n vec3 lVector = pointLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n float lightDistance = length( lVector );\n if ( testLightInRange( lightDistance, pointLight.distance ) ) {\n directLight.color = pointLight.color;\n directLight.color *= calcLightAttenuation( lightDistance, pointLight.distance, pointLight.decay );\n directLight.visible = true;\n } else {\n directLight.color = vec3( 0.0 );\n directLight.visible = false;\n }\n return directLight;\n }\n#endif\n#if NUM_SPOT_LIGHTS > 0\n struct SpotLight {\n vec3 position;\n vec3 direction;\n vec3 color;\n float distance;\n float decay;\n float angleCos;\n float penumbra;\n int shadow;\n float shadowBias;\n float shadowRadius;\n vec2 shadowMapSize;\n };\n uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ];\n IncidentLight getSpotDirectLight( const in SpotLight spotLight, const in GeometricContext geometry ) {\n IncidentLight directLight;\n vec3 lVector = spotLight.position - geometry.position;\n directLight.direction = normalize( lVector );\n float lightDistance = length( lVector );\n float spotEffect = dot( directLight.direction, spotLight.direction );\n if ( all( bvec2( spotEffect > spotLight.angleCos, testLightInRange( lightDistance, spotLight.distance ) ) ) ) {\n float spotEffect = dot( spotLight.direction, directLight.direction );\n spotEffect *= clamp( ( spotEffect - spotLight.angleCos ) / spotLight.penumbra, 0.0, 1.0 );\n directLight.color = spotLight.color;\n directLight.color *= ( spotEffect * calcLightAttenuation( lightDistance, spotLight.distance, spotLight.decay ) );\n directLight.visible = true;\n } else {\n directLight.color = vec3( 0.0 );\n directLight.visible = false;\n }\n return directLight;\n }\n#endif\n#if NUM_HEMI_LIGHTS > 0\n struct HemisphereLight {\n vec3 direction;\n vec3 skyColor;\n vec3 groundColor;\n };\n uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ];\n vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in GeometricContext geometry ) {\n float dotNL = dot( geometry.normal, hemiLight.direction );\n float hemiDiffuseWeight = 0.5 * dotNL + 0.5;\n return PI * mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight );\n }\n#endif\n#if defined( USE_ENVMAP ) && defined( STANDARD )\n vec3 getLightProbeIndirectIrradiance( const in GeometricContext geometry, const in int maxMIPLevel ) {\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryVec = flipNormal * vec3( flipEnvMap * worldNormal.x, worldNormal.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryVec, float( maxMIPLevel ) );\n #else\n vec4 envMapColor = textureCube( envMap, queryVec, float( maxMIPLevel ) );\n #endif\n #else\n vec3 envMapColor = vec3( 0.0 );\n #endif\n envMapColor.rgb = inputToLinear( envMapColor.rgb );\n return PI * envMapColor.rgb * envMapIntensity;\n }\n float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {\n float maxMIPLevelScalar = float( maxMIPLevel );\n float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( square( blinnShininessExponent ) + 1.0 );\n return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );\n }\n vec3 getLightProbeIndirectRadiance( const in GeometricContext geometry, const in float blinnShininessExponent, const in int maxMIPLevel ) {\n #ifdef ENVMAP_MODE_REFLECTION\n vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal );\n #else\n vec3 reflectVec = refract( -geometry.viewDir, geometry.normal, refractionRatio );\n #endif\n #ifdef DOUBLE_SIDED\n float flipNormal = ( float( gl_FrontFacing ) * 2.0 - 1.0 );\n #else\n float flipNormal = 1.0;\n #endif\n reflectVec = inverseTransformDirection( reflectVec, viewMatrix );\n float specularMIPLevel = getSpecularMIPLevel( blinnShininessExponent, maxMIPLevel );\n #ifdef ENVMAP_TYPE_CUBE\n vec3 queryReflectVec = flipNormal * vec3( flipEnvMap * reflectVec.x, reflectVec.yz );\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = textureCubeLodEXT( envMap, queryReflectVec, specularMIPLevel );\n #else\n vec4 envMapColor = textureCube( envMap, queryReflectVec, specularMIPLevel );\n #endif\n #elif defined( ENVMAP_TYPE_EQUIREC )\n vec2 sampleUV;\n sampleUV.y = saturate( flipNormal * reflectVec.y * 0.5 + 0.5 );\n sampleUV.x = atan( flipNormal * reflectVec.z, flipNormal * reflectVec.x ) * RECIPROCAL_PI2 + 0.5;\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, sampleUV, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, sampleUV, specularMIPLevel );\n #endif\n #elif defined( ENVMAP_TYPE_SPHERE )\n vec3 reflectView = flipNormal * normalize((viewMatrix * vec4( reflectVec, 0.0 )).xyz + vec3(0.0,0.0,1.0));\n #ifdef TEXTURE_LOD_EXT\n vec4 envMapColor = texture2DLodEXT( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #else\n vec4 envMapColor = texture2D( envMap, reflectView.xy * 0.5 + 0.5, specularMIPLevel );\n #endif\n #endif\n envMapColor.rgb = inputToLinear( envMapColor.rgb );\n return envMapColor.rgb * envMapIntensity;\n }\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_phong_fragment.glsl | |
THREE.ShaderChunk[ 'lights_phong_fragment' ] = "BlinnPhongMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb;\nmaterial.specularColor = specular;\nmaterial.specularShininess = shininess;\nmaterial.specularStrength = specularStrength;\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_fragment.glsl | |
THREE.ShaderChunk[ 'lights_phong_pars_fragment' ] = "#ifdef USE_ENVMAP\n varying vec3 vWorldPosition;\n#endif\nvarying vec3 vViewPosition;\n#ifndef FLAT_SHADED\n varying vec3 vNormal;\n#endif\nstruct BlinnPhongMaterial {\n vec3 diffuseColor;\n vec3 specularColor;\n float specularShininess;\n float specularStrength;\n};\nvoid RE_Direct_BlinnPhong( const in IncidentLight directLight, const in GeometricContext geometry, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * PI * directLight.color;\n reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n reflectedLight.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 reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\n#define RE_Direct RE_Direct_BlinnPhong\n#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong\n#define Material_LightProbeLOD( material ) (0)\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_phong_pars_vertex.glsl | |
THREE.ShaderChunk[ 'lights_phong_pars_vertex' ] = "#ifdef USE_ENVMAP\n varying vec3 vWorldPosition;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_phong_vertex.glsl | |
THREE.ShaderChunk[ 'lights_phong_vertex' ] = "#ifdef USE_ENVMAP\n vWorldPosition = worldPosition.xyz;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_standard_fragment.glsl | |
THREE.ShaderChunk[ 'lights_standard_fragment' ] = "StandardMaterial material;\nmaterial.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );\nmaterial.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );\nmaterial.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_standard_pars_fragment.glsl | |
THREE.ShaderChunk[ 'lights_standard_pars_fragment' ] = "struct StandardMaterial {\n vec3 diffuseColor;\n float specularRoughness;\n vec3 specularColor;\n};\nvoid RE_Direct_Standard( const in IncidentLight directLight, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n float dotNL = saturate( dot( geometry.normal, directLight.direction ) );\n vec3 irradiance = dotNL * PI * directLight.color;\n reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX( directLight, geometry, material.specularColor, material.specularRoughness );\n}\nvoid RE_IndirectDiffuse_Standard( const in vec3 irradiance, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );\n}\nvoid RE_IndirectSpecular_Standard( const in vec3 radiance, const in GeometricContext geometry, const in StandardMaterial material, inout ReflectedLight reflectedLight ) {\n reflectedLight.indirectSpecular += radiance * BRDF_Specular_GGX_Environment( geometry, material.specularColor, material.specularRoughness );\n}\n#define RE_Direct RE_Direct_Standard\n#define RE_IndirectDiffuse RE_IndirectDiffuse_Standard\n#define RE_IndirectSpecular RE_IndirectSpecular_Standard\n#define Material_BlinnShininessExponent( material ) GGXRoughnessToBlinnExponent( material.specularRoughness )\n"; | |
// File:src/renderers/shaders/ShaderChunk/lights_template.glsl | |
THREE.ShaderChunk[ 'lights_template' ] = "\nGeometricContext geometry;\ngeometry.position = - vViewPosition;\ngeometry.normal = normal;\ngeometry.viewDir = normalize( vViewPosition );\nIncidentLight directLight;\n#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct )\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n directLight = getPointDirectLight( pointLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( pointLight.shadow, directLight.visible ) ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct )\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n directLight = getSpotDirectLight( spotLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( spotLight.shadow, directLight.visible ) ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct )\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n directLight = getDirectionalDirectLight( directionalLight, geometry );\n #ifdef USE_SHADOWMAP\n directLight.color *= all( bvec2( directionalLight.shadow, directLight.visible ) ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n #endif\n RE_Direct( directLight, geometry, material, reflectedLight );\n }\n#endif\n#if defined( RE_IndirectDiffuse )\n vec3 irradiance = getAmbientLightIrradiance( ambientLightColor );\n #ifdef USE_LIGHTMAP\n irradiance += PI * texture2D( lightMap, vUv2 ).xyz * lightMapIntensity;\n #endif\n #if ( NUM_HEMI_LIGHTS > 0 )\n for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) {\n irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometry );\n }\n #endif\n RE_IndirectDiffuse( irradiance, geometry, material, reflectedLight );\n#endif\n#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular )\n vec3 radiance = getLightProbeIndirectRadiance( geometry, Material_BlinnShininessExponent( material ), 8 );\n RE_IndirectSpecular( radiance, geometry, material, reflectedLight );\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/linear_to_gamma_fragment.glsl | |
THREE.ShaderChunk[ 'linear_to_gamma_fragment' ] = "\n outgoingLight = linearToOutput( outgoingLight );\n"; | |
// File:src/renderers/shaders/ShaderChunk/logdepthbuf_fragment.glsl | |
THREE.ShaderChunk[ 'logdepthbuf_fragment' ] = "#if defined(USE_LOGDEPTHBUF) && defined(USE_LOGDEPTHBUF_EXT)\n gl_FragDepthEXT = log2(vFragDepth) * logDepthBufFC * 0.5;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_fragment.glsl | |
THREE.ShaderChunk[ 'logdepthbuf_pars_fragment' ] = "#ifdef USE_LOGDEPTHBUF\n uniform float logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/logdepthbuf_pars_vertex.glsl | |
THREE.ShaderChunk[ 'logdepthbuf_pars_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n #ifdef USE_LOGDEPTHBUF_EXT\n varying float vFragDepth;\n #endif\n uniform float logDepthBufFC;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/logdepthbuf_vertex.glsl | |
THREE.ShaderChunk[ 'logdepthbuf_vertex' ] = "#ifdef USE_LOGDEPTHBUF\n gl_Position.z = log2(max( EPSILON, gl_Position.w + 1.0 )) * logDepthBufFC;\n #ifdef USE_LOGDEPTHBUF_EXT\n vFragDepth = 1.0 + gl_Position.w;\n #else\n gl_Position.z = (gl_Position.z - 1.0) * gl_Position.w;\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/map_fragment.glsl | |
THREE.ShaderChunk[ 'map_fragment' ] = "#ifdef USE_MAP\n vec4 texelColor = texture2D( map, vUv );\n texelColor.xyz = inputToLinear( texelColor.xyz );\n diffuseColor *= texelColor;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/map_pars_fragment.glsl | |
THREE.ShaderChunk[ 'map_pars_fragment' ] = "#ifdef USE_MAP\n uniform sampler2D map;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/map_particle_fragment.glsl | |
THREE.ShaderChunk[ 'map_particle_fragment' ] = "#ifdef USE_MAP\n diffuseColor *= texture2D( map, vec2( gl_PointCoord.x, 1.0 - gl_PointCoord.y ) * offsetRepeat.zw + offsetRepeat.xy );\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/map_particle_pars_fragment.glsl | |
THREE.ShaderChunk[ 'map_particle_pars_fragment' ] = "#ifdef USE_MAP\n uniform vec4 offsetRepeat;\n uniform sampler2D map;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/metalnessmap_fragment.glsl | |
THREE.ShaderChunk[ 'metalnessmap_fragment' ] = "float metalnessFactor = metalness;\n#ifdef USE_METALNESSMAP\n vec4 texelMetalness = texture2D( metalnessMap, vUv );\n metalnessFactor *= texelMetalness.r;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/metalnessmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'metalnessmap_pars_fragment' ] = "#ifdef USE_METALNESSMAP\n uniform sampler2D metalnessMap;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/morphnormal_vertex.glsl | |
THREE.ShaderChunk[ 'morphnormal_vertex' ] = "#ifdef USE_MORPHNORMALS\n objectNormal += ( morphNormal0 - normal ) * morphTargetInfluences[ 0 ];\n objectNormal += ( morphNormal1 - normal ) * morphTargetInfluences[ 1 ];\n objectNormal += ( morphNormal2 - normal ) * morphTargetInfluences[ 2 ];\n objectNormal += ( morphNormal3 - normal ) * morphTargetInfluences[ 3 ];\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/morphtarget_pars_vertex.glsl | |
THREE.ShaderChunk[ 'morphtarget_pars_vertex' ] = "#ifdef USE_MORPHTARGETS\n #ifndef USE_MORPHNORMALS\n uniform float morphTargetInfluences[ 8 ];\n #else\n uniform float morphTargetInfluences[ 4 ];\n #endif\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/morphtarget_vertex.glsl | |
THREE.ShaderChunk[ 'morphtarget_vertex' ] = "#ifdef USE_MORPHTARGETS\n transformed += ( morphTarget0 - position ) * morphTargetInfluences[ 0 ];\n transformed += ( morphTarget1 - position ) * morphTargetInfluences[ 1 ];\n transformed += ( morphTarget2 - position ) * morphTargetInfluences[ 2 ];\n transformed += ( morphTarget3 - position ) * morphTargetInfluences[ 3 ];\n #ifndef USE_MORPHNORMALS\n transformed += ( morphTarget4 - position ) * morphTargetInfluences[ 4 ];\n transformed += ( morphTarget5 - position ) * morphTargetInfluences[ 5 ];\n transformed += ( morphTarget6 - position ) * morphTargetInfluences[ 6 ];\n transformed += ( morphTarget7 - position ) * morphTargetInfluences[ 7 ];\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/normal_fragment.glsl | |
THREE.ShaderChunk[ 'normal_fragment' ] = "#ifdef FLAT_SHADED\n vec3 fdx = vec3( dFdx( vViewPosition.x ), dFdx( vViewPosition.y ), dFdx( vViewPosition.z ) );\n vec3 fdy = vec3( dFdy( vViewPosition.x ), dFdy( vViewPosition.y ), dFdy( vViewPosition.z ) );\n vec3 normal = normalize( cross( fdx, fdy ) );\n#else\n vec3 normal = normalize( vNormal );\n #ifdef DOUBLE_SIDED\n normal = normal * ( -1.0 + 2.0 * float( gl_FrontFacing ) );\n #endif\n#endif\n#ifdef USE_NORMALMAP\n normal = perturbNormal2Arb( -vViewPosition, normal );\n#elif defined( USE_BUMPMAP )\n normal = perturbNormalArb( -vViewPosition, normal, dHdxy_fwd() );\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/normalmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'normalmap_pars_fragment' ] = "#ifdef USE_NORMALMAP\n uniform sampler2D normalMap;\n uniform vec2 normalScale;\n vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm ) {\n vec3 q0 = dFdx( eye_pos.xyz );\n vec3 q1 = dFdy( eye_pos.xyz );\n vec2 st0 = dFdx( vUv.st );\n vec2 st1 = dFdy( vUv.st );\n vec3 S = normalize( q0 * st1.t - q1 * st0.t );\n vec3 T = normalize( -q0 * st1.s + q1 * st0.s );\n vec3 N = normalize( surf_norm );\n vec3 mapN = texture2D( normalMap, vUv ).xyz * 2.0 - 1.0;\n mapN.xy = normalScale * mapN.xy;\n mat3 tsn = mat3( S, T, N );\n return normalize( tsn * mapN );\n }\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/project_vertex.glsl | |
THREE.ShaderChunk[ 'project_vertex' ] = "#ifdef USE_SKINNING\n vec4 mvPosition = modelViewMatrix * skinned;\n#else\n vec4 mvPosition = modelViewMatrix * vec4( transformed, 1.0 );\n#endif\ngl_Position = projectionMatrix * mvPosition;\n"; | |
// File:src/renderers/shaders/ShaderChunk/roughnessmap_fragment.glsl | |
THREE.ShaderChunk[ 'roughnessmap_fragment' ] = "float roughnessFactor = roughness;\n#ifdef USE_ROUGHNESSMAP\n vec4 texelRoughness = texture2D( roughnessMap, vUv );\n roughnessFactor *= texelRoughness.r;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/roughnessmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'roughnessmap_pars_fragment' ] = "#ifdef USE_ROUGHNESSMAP\n uniform sampler2D roughnessMap;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'shadowmap_pars_fragment' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform sampler2D pointShadowMap[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n float unpackDepth( const in vec4 rgba_depth ) {\n const vec4 bit_shift = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );\n return dot( rgba_depth, bit_shift );\n }\n float texture2DCompare( sampler2D depths, vec2 uv, float compare ) {\n return step( compare, unpackDepth( texture2D( depths, uv ) ) );\n }\n float texture2DShadowLerp( sampler2D depths, vec2 size, vec2 uv, float compare ) {\n const vec2 offset = vec2( 0.0, 1.0 );\n vec2 texelSize = vec2( 1.0 ) / size;\n vec2 centroidUV = floor( uv * size + 0.5 ) / size;\n float lb = texture2DCompare( depths, centroidUV + texelSize * offset.xx, compare );\n float lt = texture2DCompare( depths, centroidUV + texelSize * offset.xy, compare );\n float rb = texture2DCompare( depths, centroidUV + texelSize * offset.yx, compare );\n float rt = texture2DCompare( depths, centroidUV + texelSize * offset.yy, compare );\n vec2 f = fract( uv * size + 0.5 );\n float a = mix( lb, lt, f.y );\n float b = mix( rb, rt, f.y );\n float c = mix( a, b, f.x );\n return c;\n }\n float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n shadowCoord.xyz /= shadowCoord.w;\n shadowCoord.z += shadowBias;\n bvec4 inFrustumVec = bvec4 ( shadowCoord.x >= 0.0, shadowCoord.x <= 1.0, shadowCoord.y >= 0.0, shadowCoord.y <= 1.0 );\n bool inFrustum = all( inFrustumVec );\n bvec2 frustumTestVec = bvec2( inFrustum, shadowCoord.z <= 1.0 );\n bool frustumTest = all( frustumTestVec );\n if ( frustumTest ) {\n #if defined( SHADOWMAP_TYPE_PCF )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #elif defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec2 texelSize = vec2( 1.0 ) / shadowMapSize;\n float dx0 = - texelSize.x * shadowRadius;\n float dy0 = - texelSize.y * shadowRadius;\n float dx1 = + texelSize.x * shadowRadius;\n float dy1 = + texelSize.y * shadowRadius;\n return (\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy, shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) +\n texture2DShadowLerp( shadowMap, shadowMapSize, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z )\n ) * ( 1.0 / 9.0 );\n #else\n return texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z );\n #endif\n }\n return 1.0;\n }\n vec2 cubeToUV( vec3 v, float texelSizeY ) {\n vec3 absV = abs( v );\n float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) );\n absV *= scaleToCube;\n v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY );\n vec2 planar = v.xy;\n float almostATexel = 1.5 * texelSizeY;\n float almostOne = 1.0 - almostATexel;\n if ( absV.z >= almostOne ) {\n if ( v.z > 0.0 )\n planar.x = 4.0 - v.x;\n } else if ( absV.x >= almostOne ) {\n float signX = sign( v.x );\n planar.x = v.z * signX + 2.0 * signX;\n } else if ( absV.y >= almostOne ) {\n float signY = sign( v.y );\n planar.x = v.x + 2.0 * signY + 2.0;\n planar.y = v.z * signY - 2.0;\n }\n return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 );\n }\n float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowBias, float shadowRadius, vec4 shadowCoord ) {\n vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) );\n vec3 lightToPosition = shadowCoord.xyz;\n vec3 bd3D = normalize( lightToPosition );\n float dp = ( length( lightToPosition ) - shadowBias ) / 1000.0;\n #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT )\n vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y;\n return (\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) +\n texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp )\n ) * ( 1.0 / 9.0 );\n #else\n return texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp );\n #endif\n }\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/shadowmap_pars_vertex.glsl | |
THREE.ShaderChunk[ 'shadowmap_pars_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHTS ];\n varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHTS ];\n #endif\n #if NUM_SPOT_LIGHTS > 0\n uniform mat4 spotShadowMatrix[ NUM_SPOT_LIGHTS ];\n varying vec4 vSpotShadowCoord[ NUM_SPOT_LIGHTS ];\n #endif\n #if NUM_POINT_LIGHTS > 0\n uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHTS ];\n varying vec4 vPointShadowCoord[ NUM_POINT_LIGHTS ];\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/shadowmap_vertex.glsl | |
THREE.ShaderChunk[ 'shadowmap_vertex' ] = "#ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n vSpotShadowCoord[ i ] = spotShadowMatrix[ i ] * worldPosition;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * worldPosition;\n }\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/shadowmask_pars_fragment.glsl | |
THREE.ShaderChunk[ 'shadowmask_pars_fragment' ] = "float getShadowMask() {\n float shadow = 1.0;\n #ifdef USE_SHADOWMAP\n #if NUM_DIR_LIGHTS > 0\n DirectionalLight directionalLight;\n for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) {\n directionalLight = directionalLights[ i ];\n shadow *= bool( directionalLight.shadow ) ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_SPOT_LIGHTS > 0\n SpotLight spotLight;\n for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) {\n spotLight = spotLights[ i ];\n shadow *= bool( spotLight.shadow ) ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowBias, spotLight.shadowRadius, vSpotShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #if NUM_POINT_LIGHTS > 0\n PointLight pointLight;\n for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) {\n pointLight = pointLights[ i ];\n shadow *= bool( pointLight.shadow ) ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ] ) : 1.0;\n }\n #endif\n #endif\n return shadow;\n}\n"; | |
// File:src/renderers/shaders/ShaderChunk/skinbase_vertex.glsl | |
THREE.ShaderChunk[ 'skinbase_vertex' ] = "#ifdef USE_SKINNING\n mat4 boneMatX = getBoneMatrix( skinIndex.x );\n mat4 boneMatY = getBoneMatrix( skinIndex.y );\n mat4 boneMatZ = getBoneMatrix( skinIndex.z );\n mat4 boneMatW = getBoneMatrix( skinIndex.w );\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/skinning_pars_vertex.glsl | |
THREE.ShaderChunk[ 'skinning_pars_vertex' ] = "#ifdef USE_SKINNING\n uniform mat4 bindMatrix;\n uniform mat4 bindMatrixInverse;\n #ifdef BONE_TEXTURE\n uniform sampler2D boneTexture;\n uniform int boneTextureWidth;\n uniform int boneTextureHeight;\n mat4 getBoneMatrix( const in float i ) {\n float j = i * 4.0;\n float x = mod( j, float( boneTextureWidth ) );\n float y = floor( j / float( boneTextureWidth ) );\n float dx = 1.0 / float( boneTextureWidth );\n float dy = 1.0 / float( boneTextureHeight );\n y = dy * ( y + 0.5 );\n vec4 v1 = texture2D( boneTexture, vec2( dx * ( x + 0.5 ), y ) );\n vec4 v2 = texture2D( boneTexture, vec2( dx * ( x + 1.5 ), y ) );\n vec4 v3 = texture2D( boneTexture, vec2( dx * ( x + 2.5 ), y ) );\n vec4 v4 = texture2D( boneTexture, vec2( dx * ( x + 3.5 ), y ) );\n mat4 bone = mat4( v1, v2, v3, v4 );\n return bone;\n }\n #else\n uniform mat4 boneGlobalMatrices[ MAX_BONES ];\n mat4 getBoneMatrix( const in float i ) {\n mat4 bone = boneGlobalMatrices[ int(i) ];\n return bone;\n }\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/skinning_vertex.glsl | |
THREE.ShaderChunk[ 'skinning_vertex' ] = "#ifdef USE_SKINNING\n vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 );\n vec4 skinned = vec4( 0.0 );\n skinned += boneMatX * skinVertex * skinWeight.x;\n skinned += boneMatY * skinVertex * skinWeight.y;\n skinned += boneMatZ * skinVertex * skinWeight.z;\n skinned += boneMatW * skinVertex * skinWeight.w;\n skinned = bindMatrixInverse * skinned;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/skinnormal_vertex.glsl | |
THREE.ShaderChunk[ 'skinnormal_vertex' ] = "#ifdef USE_SKINNING\n mat4 skinMatrix = mat4( 0.0 );\n skinMatrix += skinWeight.x * boneMatX;\n skinMatrix += skinWeight.y * boneMatY;\n skinMatrix += skinWeight.z * boneMatZ;\n skinMatrix += skinWeight.w * boneMatW;\n skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;\n objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/specularmap_fragment.glsl | |
THREE.ShaderChunk[ 'specularmap_fragment' ] = "float specularStrength;\n#ifdef USE_SPECULARMAP\n vec4 texelSpecular = texture2D( specularMap, vUv );\n specularStrength = texelSpecular.r;\n#else\n specularStrength = 1.0;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/specularmap_pars_fragment.glsl | |
THREE.ShaderChunk[ 'specularmap_pars_fragment' ] = "#ifdef USE_SPECULARMAP\n uniform sampler2D specularMap;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/uv2_pars_fragment.glsl | |
THREE.ShaderChunk[ 'uv2_pars_fragment' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n varying vec2 vUv2;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/uv2_pars_vertex.glsl | |
THREE.ShaderChunk[ 'uv2_pars_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n attribute vec2 uv2;\n varying vec2 vUv2;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/uv2_vertex.glsl | |
THREE.ShaderChunk[ 'uv2_vertex' ] = "#if defined( USE_LIGHTMAP ) || defined( USE_AOMAP )\n vUv2 = uv2;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/uv_pars_fragment.glsl | |
THREE.ShaderChunk[ '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 varying vec2 vUv;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/uv_pars_vertex.glsl | |
THREE.ShaderChunk[ '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 varying vec2 vUv;\n uniform vec4 offsetRepeat;\n#endif\n"; | |
// File:src/renderers/shaders/ShaderChunk/uv_vertex.glsl | |
THREE.ShaderChunk[ '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 vUv = uv * offsetRepeat.zw + offsetRepeat.xy;\n#endif"; | |
// File:src/renderers/shaders/ShaderChunk/worldpos_vertex.glsl | |
THREE.ShaderChunk[ 'worldpos_vertex' ] = "#if defined( USE_ENVMAP ) || defined( PHONG ) || defined( STANDARD ) || defined( LAMBERT ) || defined ( USE_SHADOWMAP )\n #ifdef USE_SKINNING\n vec4 worldPosition = modelMatrix * skinned;\n #else\n vec4 worldPosition = modelMatrix * vec4( transformed, 1.0 );\n #endif\n#endif\n"; | |
// File:src/renderers/shaders/UniformsUtils.js | |
/** | |
* Uniform Utilities | |
*/ | |
THREE.UniformsUtils = { | |
merge: function ( uniforms ) { | |
var merged = {}; | |
for ( var u = 0; u < uniforms.length; u ++ ) { | |
var tmp = this.clone( uniforms[ u ] ); | |
for ( var p in tmp ) { | |
merged[ p ] = tmp[ p ]; | |
} | |
} | |
return merged; | |
}, | |
clone: function ( uniforms_src ) { | |
var uniforms_dst = {}; | |
for ( var u in uniforms_src ) { | |
uniforms_dst[ u ] = {}; | |
for ( var p in uniforms_src[ u ] ) { | |
var parameter_src = uniforms_src[ u ][ p ]; | |
if ( parameter_src instanceof THREE.Color || | |
parameter_src instanceof THREE.Vector2 || | |
parameter_src instanceof THREE.Vector3 || | |
parameter_src instanceof THREE.Vector4 || | |
parameter_src instanceof THREE.Matrix3 || | |
parameter_src instanceof THREE.Matrix4 || | |
parameter_src instanceof THREE.Texture ) { | |
uniforms_dst[ u ][ p ] = parameter_src.clone(); | |
} else if ( Array.isArray( parameter_src ) ) { | |
uniforms_dst[ u ][ p ] = parameter_src.slice(); | |
} else { | |
uniforms_dst[ u ][ p ] = parameter_src; | |
} | |
} | |
} | |
return uniforms_dst; | |
} | |
}; | |
// File:src/renderers/shaders/UniformsLib.js | |
/** | |
* Uniforms library for shared webgl shaders | |
*/ | |
THREE.UniformsLib = { | |
common: { | |
"diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, | |
"opacity": { type: "f", value: 1.0 }, | |
"map": { type: "t", value: null }, | |
"offsetRepeat": { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) }, | |
"specularMap": { type: "t", value: null }, | |
"alphaMap": { type: "t", value: null }, | |
"envMap": { type: "t", value: null }, | |
"flipEnvMap": { type: "f", value: - 1 }, | |
"reflectivity": { type: "f", value: 1.0 }, | |
"refractionRatio": { type: "f", value: 0.98 } | |
}, | |
aomap: { | |
"aoMap": { type: "t", value: null }, | |
"aoMapIntensity": { type: "f", value: 1 } | |
}, | |
lightmap: { | |
"lightMap": { type: "t", value: null }, | |
"lightMapIntensity": { type: "f", value: 1 } | |
}, | |
emissivemap: { | |
"emissiveMap": { type: "t", value: null } | |
}, | |
bumpmap: { | |
"bumpMap": { type: "t", value: null }, | |
"bumpScale": { type: "f", value: 1 } | |
}, | |
normalmap: { | |
"normalMap": { type: "t", value: null }, | |
"normalScale": { type: "v2", value: new THREE.Vector2( 1, 1 ) } | |
}, | |
displacementmap: { | |
"displacementMap": { type: "t", value: null }, | |
"displacementScale": { type: "f", value: 1 }, | |
"displacementBias": { type: "f", value: 0 } | |
}, | |
roughnessmap: { | |
"roughnessMap": { type: "t", value: null } | |
}, | |
metalnessmap: { | |
"metalnessMap": { type: "t", value: null } | |
}, | |
fog: { | |
"fogDensity": { type: "f", value: 0.00025 }, | |
"fogNear": { type: "f", value: 1 }, | |
"fogFar": { type: "f", value: 2000 }, | |
"fogColor": { type: "c", value: new THREE.Color( 0xffffff ) } | |
}, | |
ambient: { | |
"ambientLightColor": { type: "fv", value: [] } | |
}, | |
lights: { | |
"directionalLights": { type: "sa", value: [], properties: { | |
"direction": { type: "v3" }, | |
"color": { type: "c" }, | |
"shadow": { type: "i" }, | |
"shadowBias": { type: "f" }, | |
"shadowRadius": { type: "f" }, | |
"shadowMapSize": { type: "v2" } | |
} }, | |
"directionalShadowMap": { type: "tv", value: [] }, | |
"directionalShadowMatrix": { type: "m4v", value: [] }, | |
"spotLights": { type: "sa", value: [], properties: { | |
"color": { type: "c" }, | |
"position": { type: "v3" }, | |
"direction": { type: "v3" }, | |
"distance": { type: "f" }, | |
"angleCos": { type: "f" }, | |
"penumbra": { type: "f" }, | |
"decay": { type: "f" }, | |
"shadow": { type: "i" }, | |
"shadowBias": { type: "f" }, | |
"shadowRadius": { type: "f" }, | |
"shadowMapSize": { type: "v2" } | |
} }, | |
"spotShadowMap": { type: "tv", value: [] }, | |
"spotShadowMatrix": { type: "m4v", value: [] }, | |
"pointLights": { type: "sa", value: [], properties: { | |
"color": { type: "c" }, | |
"position": { type: "v3" }, | |
"decay": { type: "f" }, | |
"distance": { type: "f" }, | |
"shadow": { type: "i" }, | |
"shadowBias": { type: "f" }, | |
"shadowRadius": { type: "f" }, | |
"shadowMapSize": { type: "v2" } | |
} }, | |
"pointShadowMap": { type: "tv", value: [] }, | |
"pointShadowMatrix": { type: "m4v", value: [] }, | |
"hemisphereLights": { type: "sa", value: [], properties: { | |
"direction": { type: "v3" }, | |
"skyColor": { type: "c" }, | |
"groundColor": { type: "c" } | |
} } | |
}, | |
points: { | |
"diffuse": { type: "c", value: new THREE.Color( 0xeeeeee ) }, | |
"opacity": { type: "f", value: 1.0 }, | |
"size": { type: "f", value: 1.0 }, | |
"scale": { type: "f", value: 1.0 }, | |
"map": { type: "t", value: null }, | |
"offsetRepeat": { type: "v4", value: new THREE.Vector4( 0, 0, 1, 1 ) } | |
} | |
}; | |
// File:src/renderers/shaders/ShaderLib.js | |
/** | |
* Webgl Shader Library for three.js | |
* | |
* @author alteredq / http://alteredqualia.com/ | |
* @author mrdoob / http://mrdoob.com/ | |
* @author mikael emtinger / http://gomo.se/ | |
*/ | |
THREE.ShaderLib = { | |
'basic': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "common" ], | |
THREE.UniformsLib[ "aomap" ], | |
THREE.UniformsLib[ "fog" ] | |
] ), | |
vertexShader: [ | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "uv_pars_vertex" ], | |
THREE.ShaderChunk[ "uv2_pars_vertex" ], | |
THREE.ShaderChunk[ "envmap_pars_vertex" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "uv_vertex" ], | |
THREE.ShaderChunk[ "uv2_vertex" ], | |
THREE.ShaderChunk[ "color_vertex" ], | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
" #ifdef USE_ENVMAP", | |
THREE.ShaderChunk[ "beginnormal_vertex" ], | |
THREE.ShaderChunk[ "morphnormal_vertex" ], | |
THREE.ShaderChunk[ "skinnormal_vertex" ], | |
THREE.ShaderChunk[ "defaultnormal_vertex" ], | |
" #endif", | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
THREE.ShaderChunk[ "envmap_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform vec3 diffuse;", | |
"uniform float opacity;", | |
"#ifndef FLAT_SHADED", | |
" varying vec3 vNormal;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "uv_pars_fragment" ], | |
THREE.ShaderChunk[ "uv2_pars_fragment" ], | |
THREE.ShaderChunk[ "map_pars_fragment" ], | |
THREE.ShaderChunk[ "alphamap_pars_fragment" ], | |
THREE.ShaderChunk[ "aomap_pars_fragment" ], | |
THREE.ShaderChunk[ "envmap_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "specularmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "map_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "alphamap_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
THREE.ShaderChunk[ "specularmap_fragment" ], | |
" ReflectedLight reflectedLight;", | |
" reflectedLight.directDiffuse = vec3( 0.0 );", | |
" reflectedLight.directSpecular = vec3( 0.0 );", | |
" reflectedLight.indirectDiffuse = diffuseColor.rgb;", | |
" reflectedLight.indirectSpecular = vec3( 0.0 );", | |
THREE.ShaderChunk[ "aomap_fragment" ], | |
" vec3 outgoingLight = reflectedLight.indirectDiffuse;", | |
THREE.ShaderChunk[ "envmap_fragment" ], | |
THREE.ShaderChunk[ "linear_to_gamma_fragment" ], | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'lambert': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "common" ], | |
THREE.UniformsLib[ "aomap" ], | |
THREE.UniformsLib[ "lightmap" ], | |
THREE.UniformsLib[ "emissivemap" ], | |
THREE.UniformsLib[ "fog" ], | |
THREE.UniformsLib[ "ambient" ], | |
THREE.UniformsLib[ "lights" ], | |
{ | |
"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) } | |
} | |
] ), | |
vertexShader: [ | |
"#define LAMBERT", | |
"varying vec3 vLightFront;", | |
"#ifdef DOUBLE_SIDED", | |
" varying vec3 vLightBack;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "uv_pars_vertex" ], | |
THREE.ShaderChunk[ "uv2_pars_vertex" ], | |
THREE.ShaderChunk[ "envmap_pars_vertex" ], | |
THREE.ShaderChunk[ "bsdfs" ], | |
THREE.ShaderChunk[ "lights_pars" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "uv_vertex" ], | |
THREE.ShaderChunk[ "uv2_vertex" ], | |
THREE.ShaderChunk[ "color_vertex" ], | |
THREE.ShaderChunk[ "beginnormal_vertex" ], | |
THREE.ShaderChunk[ "morphnormal_vertex" ], | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "skinnormal_vertex" ], | |
THREE.ShaderChunk[ "defaultnormal_vertex" ], | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
THREE.ShaderChunk[ "envmap_vertex" ], | |
THREE.ShaderChunk[ "lights_lambert_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform vec3 diffuse;", | |
"uniform vec3 emissive;", | |
"uniform float opacity;", | |
"varying vec3 vLightFront;", | |
"#ifdef DOUBLE_SIDED", | |
" varying vec3 vLightBack;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "uv_pars_fragment" ], | |
THREE.ShaderChunk[ "uv2_pars_fragment" ], | |
THREE.ShaderChunk[ "map_pars_fragment" ], | |
THREE.ShaderChunk[ "alphamap_pars_fragment" ], | |
THREE.ShaderChunk[ "aomap_pars_fragment" ], | |
THREE.ShaderChunk[ "lightmap_pars_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_pars_fragment" ], | |
THREE.ShaderChunk[ "envmap_pars_fragment" ], | |
THREE.ShaderChunk[ "bsdfs" ], | |
THREE.ShaderChunk[ "ambient_pars" ], | |
THREE.ShaderChunk[ "lights_pars" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmask_pars_fragment" ], | |
THREE.ShaderChunk[ "specularmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
" ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", | |
" vec3 totalEmissiveLight = emissive;", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "map_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "alphamap_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
THREE.ShaderChunk[ "specularmap_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_fragment" ], | |
// accumulation | |
" reflectedLight.indirectDiffuse = getAmbientLightIrradiance( ambientLightColor );", | |
THREE.ShaderChunk[ "lightmap_fragment" ], | |
" reflectedLight.indirectDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb );", | |
" #ifdef DOUBLE_SIDED", | |
" reflectedLight.directDiffuse = ( gl_FrontFacing ) ? vLightFront : vLightBack;", | |
" #else", | |
" reflectedLight.directDiffuse = vLightFront;", | |
" #endif", | |
" reflectedLight.directDiffuse *= BRDF_Diffuse_Lambert( diffuseColor.rgb ) * getShadowMask();", | |
// modulation | |
THREE.ShaderChunk[ "aomap_fragment" ], | |
" vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveLight;", | |
THREE.ShaderChunk[ "envmap_fragment" ], | |
THREE.ShaderChunk[ "linear_to_gamma_fragment" ], | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'phong': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "common" ], | |
THREE.UniformsLib[ "aomap" ], | |
THREE.UniformsLib[ "lightmap" ], | |
THREE.UniformsLib[ "emissivemap" ], | |
THREE.UniformsLib[ "bumpmap" ], | |
THREE.UniformsLib[ "normalmap" ], | |
THREE.UniformsLib[ "displacementmap" ], | |
THREE.UniformsLib[ "fog" ], | |
THREE.UniformsLib[ "ambient" ], | |
THREE.UniformsLib[ "lights" ], | |
{ | |
"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, | |
"specular" : { type: "c", value: new THREE.Color( 0x111111 ) }, | |
"shininess": { type: "f", value: 30 } | |
} | |
] ), | |
vertexShader: [ | |
"#define PHONG", | |
"varying vec3 vViewPosition;", | |
"#ifndef FLAT_SHADED", | |
" varying vec3 vNormal;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "uv_pars_vertex" ], | |
THREE.ShaderChunk[ "uv2_pars_vertex" ], | |
THREE.ShaderChunk[ "displacementmap_pars_vertex" ], | |
THREE.ShaderChunk[ "envmap_pars_vertex" ], | |
THREE.ShaderChunk[ "lights_phong_pars_vertex" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "uv_vertex" ], | |
THREE.ShaderChunk[ "uv2_vertex" ], | |
THREE.ShaderChunk[ "color_vertex" ], | |
THREE.ShaderChunk[ "beginnormal_vertex" ], | |
THREE.ShaderChunk[ "morphnormal_vertex" ], | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "skinnormal_vertex" ], | |
THREE.ShaderChunk[ "defaultnormal_vertex" ], | |
"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED | |
" vNormal = normalize( transformedNormal );", | |
"#endif", | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "displacementmap_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
" vViewPosition = - mvPosition.xyz;", | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
THREE.ShaderChunk[ "envmap_vertex" ], | |
THREE.ShaderChunk[ "lights_phong_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"#define PHONG", | |
"uniform vec3 diffuse;", | |
"uniform vec3 emissive;", | |
"uniform vec3 specular;", | |
"uniform float shininess;", | |
"uniform float opacity;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "uv_pars_fragment" ], | |
THREE.ShaderChunk[ "uv2_pars_fragment" ], | |
THREE.ShaderChunk[ "map_pars_fragment" ], | |
THREE.ShaderChunk[ "alphamap_pars_fragment" ], | |
THREE.ShaderChunk[ "aomap_pars_fragment" ], | |
THREE.ShaderChunk[ "lightmap_pars_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_pars_fragment" ], | |
THREE.ShaderChunk[ "envmap_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "bsdfs" ], | |
THREE.ShaderChunk[ "ambient_pars" ], | |
THREE.ShaderChunk[ "lights_pars" ], | |
THREE.ShaderChunk[ "lights_phong_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_pars_fragment" ], | |
THREE.ShaderChunk[ "bumpmap_pars_fragment" ], | |
THREE.ShaderChunk[ "normalmap_pars_fragment" ], | |
THREE.ShaderChunk[ "specularmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
" ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", | |
" vec3 totalEmissiveLight = emissive;", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "map_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "alphamap_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
THREE.ShaderChunk[ "specularmap_fragment" ], | |
THREE.ShaderChunk[ "normal_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_fragment" ], | |
// accumulation | |
THREE.ShaderChunk[ "lights_phong_fragment" ], | |
THREE.ShaderChunk[ "lights_template" ], | |
// modulation | |
THREE.ShaderChunk[ "aomap_fragment" ], | |
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", | |
THREE.ShaderChunk[ "envmap_fragment" ], | |
THREE.ShaderChunk[ "linear_to_gamma_fragment" ], | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'standard': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "common" ], | |
THREE.UniformsLib[ "aomap" ], | |
THREE.UniformsLib[ "lightmap" ], | |
THREE.UniformsLib[ "emissivemap" ], | |
THREE.UniformsLib[ "bumpmap" ], | |
THREE.UniformsLib[ "normalmap" ], | |
THREE.UniformsLib[ "displacementmap" ], | |
THREE.UniformsLib[ "roughnessmap" ], | |
THREE.UniformsLib[ "metalnessmap" ], | |
THREE.UniformsLib[ "fog" ], | |
THREE.UniformsLib[ "ambient" ], | |
THREE.UniformsLib[ "lights" ], | |
{ | |
"emissive" : { type: "c", value: new THREE.Color( 0x000000 ) }, | |
"roughness": { type: "f", value: 0.5 }, | |
"metalness": { type: "f", value: 0 }, | |
"envMapIntensity" : { type: "f", value: 1 } // temporary | |
} | |
] ), | |
vertexShader: [ | |
"#define STANDARD", | |
"varying vec3 vViewPosition;", | |
"#ifndef FLAT_SHADED", | |
" varying vec3 vNormal;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "uv_pars_vertex" ], | |
THREE.ShaderChunk[ "uv2_pars_vertex" ], | |
THREE.ShaderChunk[ "displacementmap_pars_vertex" ], | |
THREE.ShaderChunk[ "envmap_pars_vertex" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_pars_vertex" ], | |
THREE.ShaderChunk[ "specularmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", // STANDARD | |
THREE.ShaderChunk[ "uv_vertex" ], | |
THREE.ShaderChunk[ "uv2_vertex" ], | |
THREE.ShaderChunk[ "color_vertex" ], | |
THREE.ShaderChunk[ "beginnormal_vertex" ], | |
THREE.ShaderChunk[ "morphnormal_vertex" ], | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "skinnormal_vertex" ], | |
THREE.ShaderChunk[ "defaultnormal_vertex" ], | |
"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED | |
" vNormal = normalize( transformedNormal );", | |
"#endif", | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "displacementmap_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
" vViewPosition = - mvPosition.xyz;", | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
THREE.ShaderChunk[ "envmap_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"#define STANDARD", | |
"uniform vec3 diffuse;", | |
"uniform vec3 emissive;", | |
"uniform float roughness;", | |
"uniform float metalness;", | |
"uniform float opacity;", | |
"uniform float envMapIntensity;", // temporary | |
"varying vec3 vViewPosition;", | |
"#ifndef FLAT_SHADED", | |
" varying vec3 vNormal;", | |
"#endif", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "uv_pars_fragment" ], | |
THREE.ShaderChunk[ "uv2_pars_fragment" ], | |
THREE.ShaderChunk[ "map_pars_fragment" ], | |
THREE.ShaderChunk[ "alphamap_pars_fragment" ], | |
THREE.ShaderChunk[ "aomap_pars_fragment" ], | |
THREE.ShaderChunk[ "lightmap_pars_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_pars_fragment" ], | |
THREE.ShaderChunk[ "envmap_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "bsdfs" ], | |
THREE.ShaderChunk[ "ambient_pars" ], | |
THREE.ShaderChunk[ "lights_pars" ], | |
THREE.ShaderChunk[ "lights_standard_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_pars_fragment" ], | |
THREE.ShaderChunk[ "bumpmap_pars_fragment" ], | |
THREE.ShaderChunk[ "normalmap_pars_fragment" ], | |
THREE.ShaderChunk[ "roughnessmap_pars_fragment" ], | |
THREE.ShaderChunk[ "metalnessmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
" ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", | |
" vec3 totalEmissiveLight = emissive;", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "map_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "alphamap_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
THREE.ShaderChunk[ "specularmap_fragment" ], | |
THREE.ShaderChunk[ "roughnessmap_fragment" ], | |
THREE.ShaderChunk[ "metalnessmap_fragment" ], | |
THREE.ShaderChunk[ "normal_fragment" ], | |
THREE.ShaderChunk[ "emissivemap_fragment" ], | |
// accumulation | |
THREE.ShaderChunk[ "lights_standard_fragment" ], | |
THREE.ShaderChunk[ "lights_template" ], | |
// modulation | |
THREE.ShaderChunk[ "aomap_fragment" ], | |
"vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", | |
THREE.ShaderChunk[ "linear_to_gamma_fragment" ], | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'points': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "points" ], | |
THREE.UniformsLib[ "fog" ] | |
] ), | |
vertexShader: [ | |
"uniform float size;", | |
"uniform float scale;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "color_vertex" ], | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
" #ifdef USE_SIZEATTENUATION", | |
" gl_PointSize = size * ( scale / - mvPosition.z );", | |
" #else", | |
" gl_PointSize = size;", | |
" #endif", | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
THREE.ShaderChunk[ "shadowmap_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform vec3 diffuse;", | |
"uniform float opacity;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "map_particle_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "shadowmap_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" vec3 outgoingLight = vec3( 0.0 );", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "map_particle_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
THREE.ShaderChunk[ "alphatest_fragment" ], | |
" outgoingLight = diffuseColor.rgb;", | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'dashed': { | |
uniforms: THREE.UniformsUtils.merge( [ | |
THREE.UniformsLib[ "common" ], | |
THREE.UniformsLib[ "fog" ], | |
{ | |
"scale" : { type: "f", value: 1 }, | |
"dashSize" : { type: "f", value: 1 }, | |
"totalSize": { type: "f", value: 2 } | |
} | |
] ), | |
vertexShader: [ | |
"uniform float scale;", | |
"attribute float lineDistance;", | |
"varying float vLineDistance;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "color_vertex" ], | |
" vLineDistance = scale * lineDistance;", | |
" vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", | |
" gl_Position = projectionMatrix * mvPosition;", | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform vec3 diffuse;", | |
"uniform float opacity;", | |
"uniform float dashSize;", | |
"uniform float totalSize;", | |
"varying float vLineDistance;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "color_pars_fragment" ], | |
THREE.ShaderChunk[ "fog_pars_fragment" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" if ( mod( vLineDistance, totalSize ) > dashSize ) {", | |
" discard;", | |
" }", | |
" vec3 outgoingLight = vec3( 0.0 );", | |
" vec4 diffuseColor = vec4( diffuse, opacity );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
THREE.ShaderChunk[ "color_fragment" ], | |
" outgoingLight = diffuseColor.rgb;", // simple shader | |
THREE.ShaderChunk[ "fog_fragment" ], | |
" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", | |
"}" | |
].join( "\n" ) | |
}, | |
'depth': { | |
uniforms: { | |
"mNear": { type: "f", value: 1.0 }, | |
"mFar" : { type: "f", value: 2000.0 }, | |
"opacity" : { type: "f", value: 1.0 } | |
}, | |
vertexShader: [ | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform float mNear;", | |
"uniform float mFar;", | |
"uniform float opacity;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
" #ifdef USE_LOGDEPTHBUF_EXT", | |
" float depth = gl_FragDepthEXT / gl_FragCoord.w;", | |
" #else", | |
" float depth = gl_FragCoord.z / gl_FragCoord.w;", | |
" #endif", | |
" float color = 1.0 - smoothstep( mNear, mFar, depth );", | |
" gl_FragColor = vec4( vec3( color ), opacity );", | |
"}" | |
].join( "\n" ) | |
}, | |
'normal': { | |
uniforms: { | |
"opacity" : { type: "f", value: 1.0 } | |
}, | |
vertexShader: [ | |
"varying vec3 vNormal;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
" vNormal = normalize( normalMatrix * normal );", | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform float opacity;", | |
"varying vec3 vNormal;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" gl_FragColor = vec4( 0.5 * normalize( vNormal ) + 0.5, opacity );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
"}" | |
].join( "\n" ) | |
}, | |
/* ------------------------------------------------------------------------- | |
// Cube map shader | |
------------------------------------------------------------------------- */ | |
'cube': { | |
uniforms: { | |
"tCube": { type: "t", value: null }, | |
"tFlip": { type: "f", value: - 1 } | |
}, | |
vertexShader: [ | |
"varying vec3 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
" vWorldPosition = transformDirection( position, modelMatrix );", | |
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform samplerCube tCube;", | |
"uniform float tFlip;", | |
"varying vec3 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
" gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
"}" | |
].join( "\n" ) | |
}, | |
/* ------------------------------------------------------------------------- | |
// Cube map shader | |
------------------------------------------------------------------------- */ | |
'equirect': { | |
uniforms: { | |
"tEquirect": { type: "t", value: null }, | |
"tFlip": { type: "f", value: - 1 } | |
}, | |
vertexShader: [ | |
"varying vec3 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
" vWorldPosition = transformDirection( position, modelMatrix );", | |
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform sampler2D tEquirect;", | |
"uniform float tFlip;", | |
"varying vec3 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"void main() {", | |
// " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", | |
"vec3 direction = normalize( vWorldPosition );", | |
"vec2 sampleUV;", | |
"sampleUV.y = saturate( tFlip * direction.y * -0.5 + 0.5 );", | |
"sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5;", | |
"gl_FragColor = texture2D( tEquirect, sampleUV );", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
"}" | |
].join( "\n" ) | |
}, | |
/* Depth encoding into RGBA texture | |
* | |
* based on SpiderGL shadow map example | |
* http://spidergl.org/example.php?id=6 | |
* | |
* originally from | |
* http://www.gamedev.net/topic/442138-packing-a-float-into-a-a8r8g8b8-texture-shader/page__whichpage__1%25EF%25BF%25BD | |
* | |
* see also | |
* http://aras-p.info/blog/2009/07/30/encoding-floats-to-rgba-the-final/ | |
*/ | |
'depthRGBA': { | |
uniforms: {}, | |
vertexShader: [ | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "logdepthbuf_vertex" ], | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], | |
"vec4 pack_depth( const in float depth ) {", | |
" const vec4 bit_shift = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", | |
" const vec4 bit_mask = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", | |
" vec4 res = mod( depth * bit_shift * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", | |
" res -= res.xxyz * bit_mask;", | |
" return res;", | |
"}", | |
"void main() {", | |
THREE.ShaderChunk[ "logdepthbuf_fragment" ], | |
" #ifdef USE_LOGDEPTHBUF_EXT", | |
" gl_FragData[ 0 ] = pack_depth( gl_FragDepthEXT );", | |
" #else", | |
" gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z );", | |
" #endif", | |
//"gl_FragData[ 0 ] = pack_depth( gl_FragCoord.z / gl_FragCoord.w );", | |
//"float z = ( ( gl_FragCoord.z / gl_FragCoord.w ) - 3.0 ) / ( 4000.0 - 3.0 );", | |
//"gl_FragData[ 0 ] = pack_depth( z );", | |
//"gl_FragData[ 0 ] = vec4( z, z, z, 1.0 );", | |
"}" | |
].join( "\n" ) | |
}, | |
'distanceRGBA': { | |
uniforms: { | |
"lightPos": { type: "v3", value: new THREE.Vector3( 0, 0, 0 ) } | |
}, | |
vertexShader: [ | |
"varying vec4 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
THREE.ShaderChunk[ "morphtarget_pars_vertex" ], | |
THREE.ShaderChunk[ "skinning_pars_vertex" ], | |
"void main() {", | |
THREE.ShaderChunk[ "skinbase_vertex" ], | |
THREE.ShaderChunk[ "begin_vertex" ], | |
THREE.ShaderChunk[ "morphtarget_vertex" ], | |
THREE.ShaderChunk[ "skinning_vertex" ], | |
THREE.ShaderChunk[ "project_vertex" ], | |
THREE.ShaderChunk[ "worldpos_vertex" ], | |
"vWorldPosition = worldPosition;", | |
"}" | |
].join( "\n" ), | |
fragmentShader: [ | |
"uniform vec3 lightPos;", | |
"varying vec4 vWorldPosition;", | |
THREE.ShaderChunk[ "common" ], | |
"vec4 pack1K ( float depth ) {", | |
" depth /= 1000.0;", | |
" const vec4 bitSh = vec4( 256.0 * 256.0 * 256.0, 256.0 * 256.0, 256.0, 1.0 );", | |
" const vec4 bitMsk = vec4( 0.0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0 );", | |
" vec4 res = mod( depth * bitSh * vec4( 255 ), vec4( 256 ) ) / vec4( 255 );", | |
" res -= res.xxyz * bitMsk;", | |
" return res; ", | |
"}", | |
"float unpack1K ( vec4 color ) {", | |
" const vec4 bitSh = vec4( 1.0 / ( 256.0 * 256.0 * 256.0 ), 1.0 / ( 256.0 * 256.0 ), 1.0 / 256.0, 1.0 );", | |
" return dot( color, bitSh ) * 1000.0;", | |
"}", | |
"void main () {", | |
" gl_FragColor = pack1K( length( vWorldPosition.xyz - lightPos.xyz ) );", | |
"}" | |
].join( "\n" ) | |
} | |
}; | |
// File:src/renderers/WebGLRenderer.js | |
/** | |
* @author supereggbert / http://www.paulbrunt.co.uk/ | |
* @author mrdoob / http://mrdoob.com/ | |
* @author alteredq / http://alteredqualia.com/ | |
* @author szimek / https://github.com/szimek/ | |
*/ | |
THREE.WebGLRenderer = function ( parameters ) { | |
console.log( 'THREE.WebGLRenderer', THREE.REVISION ); | |
parameters = parameters || {}; | |
var _canvas = parameters.canvas !== undefined ? parameters.canvas : document.createElement( 'canvas' ), | |
_context = parameters.context !== undefined ? parameters.context : null, | |
_alpha = parameters.alpha !== undefined ? parameters.alpha : false, | |
_depth = parameters.depth !== undefined ? parameters.depth : true, | |
_stencil = parameters.stencil !== undefined ? parameters.stencil : true, | |
_antialias = parameters.antialias !== undefined ? parameters.antialias : false, | |
_premultipliedAlpha = parameters.premultipliedAlpha !== undefined ? parameters.premultipliedAlpha : true, | |
_preserveDrawingBuffer = parameters.preserveDrawingBuffer !== undefined ? parameters.preserveDrawingBuffer : false; | |
var lights = []; | |
var opaqueObjects = []; | |
var opaqueObjectsLastIndex = - 1; | |
var transparentObjects = []; | |
var transparentObjectsLastIndex = - 1; | |
var morphInfluences = new Float32Array( 8 ); | |
var sprites = []; | |
var lensFlares = []; | |
// public properties | |
this.domElement = _canvas; | |
this.context = null; | |
// clearing | |
this.autoClear = true; | |
this.autoClearColor = true; | |
this.autoClearDepth = true; | |
this.autoClearStencil = true; | |
// scene graph | |
this.sortObjects = true; | |
// physically based shading | |
this.gammaFactor = 2.0; // for backwards compatibility | |
this.gammaInput = false; | |
this.gammaOutput = false; | |
// morphs | |
this.maxMorphTargets = 8; | |
this.maxMorphNormals = 4; | |
// flags | |
this.autoScaleCubemaps = true; | |
// internal properties | |
var _this = this, | |
// internal state cache | |
_currentProgram = null, | |
_currentRenderTarget = null, | |
_currentFramebuffer = null, | |
_currentMaterialId = - 1, | |
_currentGeometryProgram = '', | |
_currentCamera = null, | |
_currentScissor = new THREE.Vector4(), | |
_currentScissorTest = null, | |
_currentViewport = new THREE.Vector4(), | |
// | |
_usedTextureUnits = 0, | |
// | |
_clearColor = new THREE.Color( 0x000000 ), | |
_clearAlpha = 0, | |
_width = _canvas.width, | |
_height = _canvas.height, | |
_pixelRatio = 1, | |
_scissor = new THREE.Vector4( 0, 0, _width, _height ), | |
_scissorTest = false, | |
_viewport = new THREE.Vector4( 0, 0, _width, _height ), | |
// frustum | |
_frustum = new THREE.Frustum(), | |
// camera matrices cache | |
_projScreenMatrix = new THREE.Matrix4(), | |
_vector3 = new THREE.Vector3(), | |
// light arrays cache | |
_lights = { | |
hash: '', | |
ambient: [ 0, 0, 0 ], | |
directional: [], | |
directionalShadowMap: [], | |
directionalShadowMatrix: [], | |
spot: [], | |
spotShadowMap: [], | |
spotShadowMatrix: [], | |
point: [], | |
pointShadowMap: [], | |
pointShadowMatrix: [], | |
hemi: [], | |
shadows: [], | |
shadowsPointLight: 0 | |
}, | |
// info | |
_infoMemory = { | |
geometries: 0, | |
textures: 0 | |
}, | |
_infoRender = { | |
calls: 0, | |
vertices: 0, | |
faces: 0, | |
points: 0 | |
}; | |
this.info = { | |
render: _infoRender, | |
memory: _infoMemory, | |
programs: null | |
}; | |
// initialize | |
var _gl; | |
try { | |
var attributes = { | |
alpha: _alpha, | |
depth: _depth, | |
stencil: _stencil, | |
antialias: _antialias, | |
premultipliedAlpha: _premultipliedAlpha, | |
preserveDrawingBuffer: _preserveDrawingBuffer | |
}; | |
_gl = _context || _canvas.getContext( 'webgl', attributes ) || _canvas.getContext( 'experimental-webgl', attributes ); | |
if ( _gl === null ) { | |
if ( _canvas.getContext( 'webgl' ) !== null ) { | |
throw 'Error creating WebGL context with your selected attributes.'; | |
} else { | |
throw 'Error creating WebGL context.'; | |
} | |
} | |
_canvas.addEventListener( 'webglcontextlost', onContextLost, false ); | |
} catch ( error ) { | |
console.error( 'THREE.WebGLRenderer: ' + error ); | |
} | |
var extensions = new THREE.WebGLExtensions( _gl ); | |
extensions.get( 'OES_texture_float' ); | |
extensions.get( 'OES_texture_float_linear' ); | |
extensions.get( 'OES_texture_half_float' ); | |
extensions.get( 'OES_texture_half_float_linear' ); | |
extensions.get( 'OES_standard_derivatives' ); | |
extensions.get( 'ANGLE_instanced_arrays' ); | |
if ( extensions.get( 'OES_element_index_uint' ) ) { | |
THREE.BufferGeometry.MaxIndex = 4294967296; | |
} | |
var capabilities = new THREE.WebGLCapabilities( _gl, extensions, parameters ); | |
var state = new THREE.WebGLState( _gl, extensions, paramThreeToGL ); | |
var properties = new THREE.WebGLProperties(); | |
var objects = new THREE.WebGLObjects( _gl, properties, this.info ); | |
var programCache = new THREE.WebGLPrograms( this, capabilities ); | |
var lightCache = new THREE.WebGLLights(); | |
this.info.programs = programCache.programs; | |
var bufferRenderer = new THREE.WebGLBufferRenderer( _gl, extensions, _infoRender ); | |
var indexedBufferRenderer = new THREE.WebGLIndexedBufferRenderer( _gl, extensions, _infoRender ); | |
// | |
function getTargetPixelRatio() { | |
return _currentRenderTarget === null ? _pixelRatio : 1; | |
} | |
function glClearColor( r, g, b, a ) { | |
if ( _premultipliedAlpha === true ) { | |
r *= a; g *= a; b *= a; | |
} | |
state.clearColor( r, g, b, a ); | |
} | |
function setDefaultGLState() { | |
state.init(); | |
state.scissor( _currentScissor.copy( _scissor ).multiplyScalar( _pixelRatio ) ); | |
state.viewport( _currentViewport.copy( _viewport ).multiplyScalar( _pixelRatio ) ); | |
glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); | |
} | |
function resetGLState() { | |
_currentProgram = null; | |
_currentCamera = null; | |
_currentGeometryProgram = ''; | |
_currentMaterialId = - 1; | |
state.reset(); | |
} | |
setDefaultGLState(); | |
this.context = _gl; | |
this.capabilities = capabilities; | |
this.extensions = extensions; | |
this.properties = properties; | |
this.state = state; | |
// shadow map | |
var shadowMap = new THREE.WebGLShadowMap( this, _lights, objects ); | |
this.shadowMap = shadowMap; | |
// Plugins | |
var spritePlugin = new THREE.SpritePlugin( this, sprites ); | |
var lensFlarePlugin = new THREE.LensFlarePlugin( this, lensFlares ); | |
// API | |
this.getContext = function () { | |
return _gl; | |
}; | |
this.getContextAttributes = function () { | |
return _gl.getContextAttributes(); | |
}; | |
this.forceContextLoss = function () { | |
extensions.get( 'WEBGL_lose_context' ).loseContext(); | |
}; | |
this.getMaxAnisotropy = ( function () { | |
var value; | |
return function getMaxAnisotropy() { | |
if ( value !== undefined ) return value; | |
var extension = extensions.get( 'EXT_texture_filter_anisotropic' ); | |
if ( extension !== null ) { | |
value = _gl.getParameter( extension.MAX_TEXTURE_MAX_ANISOTROPY_EXT ); | |
} else { | |
value = 0; | |
} | |
return value; | |
}; | |
} )(); | |
this.getPrecision = function () { | |
return capabilities.precision; | |
}; | |
this.getPixelRatio = function () { | |
return _pixelRatio; | |
}; | |
this.setPixelRatio = function ( value ) { | |
if ( value === undefined ) return; | |
_pixelRatio = value; | |
this.setSize( _viewport.z, _viewport.w, false ); | |
}; | |
this.getSize = function () { | |
return { | |
width: _width, | |
height: _height | |
}; | |
}; | |
this.setSize = function ( width, height, updateStyle ) { | |
_width = width; | |
_height = height; | |
_canvas.width = width * _pixelRatio; | |
_canvas.height = height * _pixelRatio; | |
if ( updateStyle !== false ) { | |
_canvas.style.width = width + 'px'; | |
_canvas.style.height = height + 'px'; | |
} | |
this.setViewport( 0, 0, width, height ); | |
}; | |
this.setViewport = function ( x, y, width, height ) { | |
state.viewport( _viewport.set( x, y, width, height ) ); | |
}; | |
this.setScissor = function ( x, y, width, height ) { | |
state.scissor( _scissor.set( x, y, width, height ) ); | |
}; | |
this.setScissorTest = function ( boolean ) { | |
state.setScissorTest( _scissorTest = boolean ); | |
}; | |
// Clearing | |
this.getClearColor = function () { | |
return _clearColor; | |
}; | |
this.setClearColor = function ( color, alpha ) { | |
_clearColor.set( color ); | |
_clearAlpha = alpha !== undefined ? alpha : 1; | |
glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); | |
}; | |
this.getClearAlpha = function () { | |
return _clearAlpha; | |
}; | |
this.setClearAlpha = function ( alpha ) { | |
_clearAlpha = alpha; | |
glClearColor( _clearColor.r, _clearColor.g, _clearColor.b, _clearAlpha ); | |
}; | |
this.clear = function ( color, depth, stencil ) { | |
var bits = 0; | |
if ( color === undefined || color ) bits |= _gl.COLOR_BUFFER_BIT; | |
if ( depth === undefined || depth ) bits |= _gl.DEPTH_BUFFER_BIT; | |
if ( stencil === undefined || stencil ) bits |= _gl.STENCIL_BUFFER_BIT; | |
_gl.clear( bits ); | |
}; | |
this.clearColor = function () { | |
this.clear( true, false, false ); | |
}; | |
this.clearDepth = function () { | |
this.clear( false, true, false ); | |
}; | |
this.clearStencil = function () { | |
this.clear( false, false, true ); | |
}; | |
this.clearTarget = function ( renderTarget, color, depth, stencil ) { | |
this.setRenderTarget( renderTarget ); | |
this.clear( color, depth, stencil ); | |
}; | |
// Reset | |
this.resetGLState = resetGLState; | |
this.dispose = function() { | |
_canvas.removeEventListener( 'webglcontextlost', onContextLost, false ); | |
}; | |
// Events | |
function onContextLost( event ) { | |
event.preventDefault(); | |
resetGLState(); | |
setDefaultGLState(); | |
properties.clear(); | |
} | |
function onTextureDispose( event ) { | |
var texture = event.target; | |
texture.removeEventListener( 'dispose', onTextureDispose ); | |
deallocateTexture( texture ); | |
_infoMemory.textures --; | |
} | |
function onRenderTargetDispose( event ) { | |
var renderTarget = event.target; | |
renderTarget.removeEventListener( 'dispose', onRenderTargetDispose ); | |
deallocateRenderTarget( renderTarget ); | |
_infoMemory.textures --; | |
} | |
function onMaterialDispose( event ) { | |
var material = event.target; | |
material.removeEventListener( 'dispose', onMaterialDispose ); | |
deallocateMaterial( material ); | |
} | |
// Buffer deallocation | |
function deallocateTexture( texture ) { | |
var textureProperties = properties.get( texture ); | |
if ( texture.image && textureProperties.__image__webglTextureCube ) { | |
// cube texture | |
_gl.deleteTexture( textureProperties.__image__webglTextureCube ); | |
} else { | |
// 2D texture | |
if ( textureProperties.__webglInit === undefined ) return; | |
_gl.deleteTexture( textureProperties.__webglTexture ); | |
} | |
// remove all webgl properties | |
properties.delete( texture ); | |
} | |
function deallocateRenderTarget( renderTarget ) { | |
var renderTargetProperties = properties.get( renderTarget ); | |
var textureProperties = properties.get( renderTarget.texture ); | |
if ( ! renderTarget || textureProperties.__webglTexture === undefined ) return; | |
_gl.deleteTexture( textureProperties.__webglTexture ); | |
if ( renderTarget instanceof THREE.WebGLRenderTargetCube ) { | |
for ( var i = 0; i < 6; i ++ ) { | |
_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer[ i ] ); | |
_gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer[ i ] ); | |
} | |
} else { | |
_gl.deleteFramebuffer( renderTargetProperties.__webglFramebuffer ); | |
_gl.deleteRenderbuffer( renderTargetProperties.__webglDepthbuffer ); | |
} | |
properties.delete( renderTarget.texture ); | |
properties.delete( renderTarget ); | |
} | |
function deallocateMaterial( material ) { | |
releaseMaterialProgramReference( material ); | |
properties.delete( material ); | |
} | |
function releaseMaterialProgramReference( material ) { | |
var programInfo = properties.get( material ).program; | |
material.program = undefined; | |
if ( programInfo !== undefined ) { | |
programCache.releaseProgram( programInfo ); | |
} | |
} | |
// Buffer rendering | |
this.renderBufferImmediate = function ( object, program, material ) { | |
state.initAttributes(); | |
var buffers = properties.get( object ); | |
if ( object.hasPositions && ! buffers.position ) buffers.position = _gl.createBuffer(); | |
if ( object.hasNormals && ! buffers.normal ) buffers.normal = _gl.createBuffer(); | |
if ( object.hasUvs && ! buffers.uv ) buffers.uv = _gl.createBuffer(); | |
if ( object.hasColors && ! buffers.color ) buffers.color = _gl.createBuffer(); | |
var attributes = program.getAttributes(); | |
if ( object.hasPositions ) { | |
_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.position ); | |
_gl.bufferData( _gl.ARRAY_BUFFER, object.positionArray, _gl.DYNAMIC_DRAW ); | |
state.enableAttribute( attributes.position ); | |
_gl.vertexAttribPointer( attributes.position, 3, _gl.FLOAT, false, 0, 0 ); | |
} | |
if ( object.hasNormals ) { | |
_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.normal ); | |
if ( material.type !== 'MeshPhongMaterial' && material.type !== 'MeshStandardMaterial' && material.shading === THREE.FlatShading ) { | |
for ( var i = 0, l = object.count * 3; i < l; i += 9 ) { | |
var array = object.normalArray; | |
var nx = ( array[ i + 0 ] + array[ i + 3 ] + array[ i + 6 ] ) / 3; | |
var ny = ( array[ i + 1 ] + array[ i + 4 ] + array[ i + 7 ] ) / 3; | |
var nz = ( array[ i + 2 ] + array[ i + 5 ] + array[ i + 8 ] ) / 3; | |
array[ i + 0 ] = nx; | |
array[ i + 1 ] = ny; | |
array[ i + 2 ] = nz; | |
array[ i + 3 ] = nx; | |
array[ i + 4 ] = ny; | |
array[ i + 5 ] = nz; | |
array[ i + 6 ] = nx; | |
array[ i + 7 ] = ny; | |
array[ i + 8 ] = nz; | |
} | |
} | |
_gl.bufferData( _gl.ARRAY_BUFFER, object.normalArray, _gl.DYNAMIC_DRAW ); | |
state.enableAttribute( attributes.normal ); | |
_gl.vertexAttribPointer( attributes.normal, 3, _gl.FLOAT, false, 0, 0 ); | |
} | |
if ( object.hasUvs && material.map ) { | |
_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.uv ); | |
_gl.bufferData( _gl.ARRAY_BUFFER, object.uvArray, _gl.DYNAMIC_DRAW ); | |
state.enableAttribute( attributes.uv ); | |
_gl.vertexAttribPointer( attributes.uv, 2, _gl.FLOAT, false, 0, 0 ); | |
} | |
if ( object.hasColors && material.vertexColors !== THREE.NoColors ) { | |
_gl.bindBuffer( _gl.ARRAY_BUFFER, buffers.color ); | |
_gl.bufferData( _gl.ARRAY_BUFFER, object.colorArray, _gl.DYNAMIC_DRAW ); | |
state.enableAttribute( attributes.color ); | |
_gl.vertexAttribPointer( attributes.color, 3, _gl.FLOAT, false, 0, 0 ); | |
} | |
state.disableUnusedAttributes(); | |
_gl.drawArrays( _gl.TRIANGLES, 0, object.count ); | |
object.count = 0; | |
}; | |
this.renderBufferDirect = function ( camera, fog, geometry, material, object, group ) { | |
setMaterial( material ); | |
var program = setProgram( camera, fog, material, object ); | |
var updateBuffers = false; | |
var geometryProgram = geometry.id + '_' + program.id + '_' + material.wireframe; | |
if ( geometryProgram !== _currentGeometryProgram ) { | |
_currentGeometryProgram = geometryProgram; | |
updateBuffers = true; | |
} | |
// morph targets | |
var morphTargetInfluences = object.morphTargetInfluences; | |
if ( morphTargetInfluences !== undefined ) { | |
var activeInfluences = []; | |
for ( var i = 0, l = morphTargetInfluences.length; i < l; i ++ ) { | |
var influence = morphTargetInfluences[ i ]; | |
activeInfluences.push( [ influence, i ] ); | |
} | |
activeInfluences.sort( absNumericalSort ); | |
if ( activeInfluences.length > 8 ) { | |
activeInfluences.length = 8; | |
} | |
var morphAttributes = geometry.morphAttributes; | |
for ( var i = 0, l = activeInfluences.length; i < l; i ++ ) { | |
var influence = activeInfluences[ i ]; | |
morphInfluences[ i ] = influence[ 0 ]; | |
if ( influence[ 0 ] !== 0 ) { | |
var index = influence[ 1 ]; | |
if ( material.morphTargets === true && morphAttributes.position ) geometry.addAttribute( 'morphTarget' + i, morphAttributes.position[ index ] ); | |
if ( material.morphNormals === true && morphAttributes.normal ) geometry.addAttribute( 'morphNormal' + i, morphAttributes.normal[ index ] ); | |
} else { | |
if ( material.morphTargets === true ) geometry.removeAttribute( 'morphTarget' + i ); | |
if ( material.morphNormals === true ) geometry.removeAttribute( 'morphNormal' + i ); | |
} | |
} | |
var uniforms = program.getUniforms(); | |
if ( uniforms.morphTargetInfluences !== null ) { | |
_gl.uniform1fv( uniforms.morphTargetInfluences, morphInfluences ); | |
} | |
updateBuffers = true; | |
} | |
// | |
var index = geometry.index; | |
var position = geometry.attributes.position; | |
if ( material.wireframe === true ) { | |
index = objects.getWireframeAttribute( geometry ); | |
} | |
var renderer; | |
if ( index !== null ) { | |
renderer = indexedBufferRenderer; | |
renderer.setIndex( index ); | |
} else { | |
renderer = bufferRenderer; | |
} | |
if ( updateBuffers ) { | |
setupVertexAttributes( material, program, geometry ); | |
if ( index !== null ) { | |
_gl.bindBuffer( _gl.ELEMENT_ARRAY_BUFFER, objects.getAttributeBuffer( index ) ); | |
} | |
} | |
// | |
var dataStart = 0; | |
var dataCount = Infinity; | |
if ( index !== null ) { | |
dataCount = index.count; | |
} else if ( position !== undefined ) { | |
dataCount = position.count; | |
} | |
var rangeStart = geometry.drawRange.start; | |
var rangeCount = geometry.drawRange.count; | |
var groupStart = group !== null ? group.start : 0; | |
var groupCount = group !== null ? group.count : Infinity; | |
var drawStart = Math.max( dataStart, rangeStart, groupStart ); | |
var drawEnd = Math.min( dataStart + dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1; | |
var drawCount = Math.max( 0, drawEnd - drawStart + 1 ); | |
// | |
if ( object instanceof THREE.Mesh ) { | |
if ( material.wireframe === true ) { | |
state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() ); | |
renderer.setMode( _gl.LINES ); | |
} else { | |
switch ( object.drawMode ) { | |
case THREE.TrianglesDrawMode: | |
renderer.setMode( _gl.TRIANGLES ); | |
break; | |
case THREE.TriangleStripDrawMode: | |
renderer.setMode( _gl.TRIANGLE_STRIP ); | |
break; | |
case THREE.TriangleFanDrawMode: | |
renderer.setMode( _gl.TRIANGLE_FAN ); | |
break; | |
} | |
} | |
} else if ( object instanceof THREE.Line ) { | |
var lineWidth = material.linewidth; | |
if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Material | |
state.setLineWidth( lineWidth * getTargetPixelRatio() ); | |
if ( object instanceof THREE.LineSegments ) { | |
renderer.setMode( _gl.LINES ); | |
} else { | |
renderer.setMode( _gl.LINE_STRIP ); | |
} | |
} else if ( object instanceof THREE.Points ) { | |
renderer.setMode( _gl.POINTS ); | |
} | |
if ( geometry instanceof THREE.InstancedBufferGeometry && geometry.maxInstancedCount > 0 ) { | |
renderer.renderInstances( geometry, drawStart, drawCount ); | |
} else { | |
renderer.render( drawStart, drawCount ); | |
} | |
}; | |
function setupVertexAttributes( material, program, geometry, startIndex ) { | |
var extension; | |
if ( geometry instanceof THREE.InstancedBufferGeometry ) { | |
extension = extensions.get( 'ANGLE_instanced_arrays' ); | |
if ( extension === null ) { | |
console.error( 'THREE.WebGLRenderer.setupVertexAttributes: using THREE.InstancedBufferGeometry but hard |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment