├── deps
├── Stats.js
├── Three.js
└── d3.v2.js
├── examples
└── dot.html
└── lib
└── d3-threeD.js
/deps/Stats.js:
--------------------------------------------------------------------------------
1 | // stats.js r8 - http://github.com/mrdoob/stats.js
2 | var Stats=function(){var h,a,n=0,o=0,i=Date.now(),u=i,p=i,l=0,q=1E3,r=0,e,j,f,b=[[16,16,48],[0,255,255]],m=0,s=1E3,t=0,d,k,g,c=[[16,48,16],[0,255,0]];h=document.createElement("div");h.style.cursor="pointer";h.style.width="80px";h.style.opacity="0.9";h.style.zIndex="10001";h.addEventListener("mousedown",function(a){a.preventDefault();n=(n+1)%2;n==0?(e.style.display="block",d.style.display="none"):(e.style.display="none",d.style.display="block")},!1);e=document.createElement("div");e.style.textAlign=
3 | "left";e.style.lineHeight="1.2em";e.style.backgroundColor="rgb("+Math.floor(b[0][0]/2)+","+Math.floor(b[0][1]/2)+","+Math.floor(b[0][2]/2)+")";e.style.padding="0 0 3px 3px";h.appendChild(e);j=document.createElement("div");j.style.fontFamily="Helvetica, Arial, sans-serif";j.style.fontSize="9px";j.style.color="rgb("+b[1][0]+","+b[1][1]+","+b[1][2]+")";j.style.fontWeight="bold";j.innerHTML="FPS";e.appendChild(j);f=document.createElement("div");f.style.position="relative";f.style.width="74px";f.style.height=
4 | "30px";f.style.backgroundColor="rgb("+b[1][0]+","+b[1][1]+","+b[1][2]+")";for(e.appendChild(f);f.children.length<74;)a=document.createElement("span"),a.style.width="1px",a.style.height="30px",a.style.cssFloat="left",a.style.backgroundColor="rgb("+b[0][0]+","+b[0][1]+","+b[0][2]+")",f.appendChild(a);d=document.createElement("div");d.style.textAlign="left";d.style.lineHeight="1.2em";d.style.backgroundColor="rgb("+Math.floor(c[0][0]/2)+","+Math.floor(c[0][1]/2)+","+Math.floor(c[0][2]/2)+")";d.style.padding=
5 | "0 0 3px 3px";d.style.display="none";h.appendChild(d);k=document.createElement("div");k.style.fontFamily="Helvetica, Arial, sans-serif";k.style.fontSize="9px";k.style.color="rgb("+c[1][0]+","+c[1][1]+","+c[1][2]+")";k.style.fontWeight="bold";k.innerHTML="MS";d.appendChild(k);g=document.createElement("div");g.style.position="relative";g.style.width="74px";g.style.height="30px";g.style.backgroundColor="rgb("+c[1][0]+","+c[1][1]+","+c[1][2]+")";for(d.appendChild(g);g.children.length<74;)a=document.createElement("span"),
6 | a.style.width="1px",a.style.height=Math.random()*30+"px",a.style.cssFloat="left",a.style.backgroundColor="rgb("+c[0][0]+","+c[0][1]+","+c[0][2]+")",g.appendChild(a);return{domElement:h,update:function(){i=Date.now();m=i-u;s=Math.min(s,m);t=Math.max(t,m);k.textContent=m+" MS ("+s+"-"+t+")";var a=Math.min(30,30-m/200*30);g.appendChild(g.firstChild).style.height=a+"px";u=i;o++;if(i>p+1E3)l=Math.round(o*1E3/(i-p)),q=Math.min(q,l),r=Math.max(r,l),j.textContent=l+" FPS ("+q+"-"+r+")",a=Math.min(30,30-l/
7 | 100*30),f.appendChild(f.firstChild).style.height=a+"px",p=i,o=0}}};
8 |
9 |
--------------------------------------------------------------------------------
/examples/dot.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Dot Plot
5 |
6 |
7 |
8 |
9 |
32 |
33 |
34 |
105 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/lib/d3-threeD.js:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 | * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 |
5 |
6 | function d3threeD(exports) {
7 |
8 | const DEGS_TO_RADS = Math.PI / 180,
9 | UNIT_SIZE = 100;
10 |
11 | const DIGIT_0 = 48, DIGIT_9 = 57, COMMA = 44, SPACE = 32, PERIOD = 46,
12 | MINUS = 45;
13 | function transformSVGPath(pathStr) {
14 | var path = new THREE.Shape();
15 |
16 | var idx = 1, len = pathStr.length, activeCmd,
17 | x = 0, y = 0, nx = 0, ny = 0, firstX = null, firstY = null,
18 | x1 = 0, x2 = 0, y1 = 0, y2 = 0,
19 | rx = 0, ry = 0, xar = 0, laf = 0, sf = 0, cx, cy;
20 |
21 | function eatNum() {
22 | var sidx, c, isFloat = false, s;
23 | // eat delims
24 | while (idx < len) {
25 | c = pathStr.charCodeAt(idx);
26 | if (c !== COMMA && c !== SPACE)
27 | break;
28 | idx++;
29 | }
30 | if (c === MINUS)
31 | sidx = idx++;
32 | else
33 | sidx = idx;
34 | // eat number
35 | while (idx < len) {
36 | c = pathStr.charCodeAt(idx);
37 | if (DIGIT_0 <= c && c <= DIGIT_9) {
38 | idx++;
39 | continue;
40 | }
41 | else if (c === PERIOD) {
42 | idx++;
43 | isFloat = true;
44 | continue;
45 | }
46 |
47 | s = pathStr.substring(sidx, idx);
48 | return isFloat ? parseFloat(s) : parseInt(s);
49 | }
50 |
51 | s = pathStr.substring(sidx);
52 | return isFloat ? parseFloat(s) : parseInt(s);
53 | }
54 |
55 | function nextIsNum() {
56 | var c;
57 | // do permanently eat any delims...
58 | while (idx < len) {
59 | c = pathStr.charCodeAt(idx);
60 | if (c !== COMMA && c !== SPACE)
61 | break;
62 | idx++;
63 | }
64 | c = pathStr.charCodeAt(idx);
65 | return (c === MINUS || (DIGIT_0 <= c && c <= DIGIT_9));
66 | }
67 |
68 | var canRepeat;
69 | activeCmd = pathStr[0];
70 | while (idx <= len) {
71 | canRepeat = true;
72 | switch (activeCmd) {
73 | // moveto commands, become lineto's if repeated
74 | case 'M':
75 | x = eatNum();
76 | y = eatNum();
77 | path.moveTo(x, y);
78 | activeCmd = 'L';
79 | break;
80 | case 'm':
81 | x += eatNum();
82 | y += eatNum();
83 | path.moveTo(x, y);
84 | activeCmd = 'l';
85 | break;
86 | case 'Z':
87 | case 'z':
88 | canRepeat = false;
89 | if (x !== firstX || y !== firstY)
90 | path.lineTo(firstX, firstY);
91 | break;
92 | // - lines!
93 | case 'L':
94 | case 'H':
95 | case 'V':
96 | nx = (activeCmd === 'V') ? x : eatNum();
97 | ny = (activeCmd === 'H') ? y : eatNum();
98 | path.lineTo(nx, ny);
99 | x = nx;
100 | y = ny;
101 | break;
102 | case 'l':
103 | case 'h':
104 | case 'v':
105 | nx = (activeCmd === 'v') ? x : (x + eatNum());
106 | ny = (activeCmd === 'h') ? y : (y + eatNum());
107 | path.lineTo(nx, ny);
108 | x = nx;
109 | y = ny;
110 | break;
111 | // - cubic bezier
112 | case 'C':
113 | x1 = eatNum(); y1 = eatNum();
114 | case 'S':
115 | if (activeCmd === 'S') {
116 | x1 = 2 * x - x2; y1 = 2 * y - y2;
117 | }
118 | x2 = eatNum();
119 | y2 = eatNum();
120 | nx = eatNum();
121 | ny = eatNum();
122 | path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
123 | x = nx; y = ny;
124 | break;
125 | case 'c':
126 | x1 = x + eatNum();
127 | y1 = y + eatNum();
128 | case 's':
129 | if (activeCmd === 's') {
130 | x1 = 2 * x - x2;
131 | y1 = 2 * y - y2;
132 | }
133 | x2 = x + eatNum();
134 | y2 = y + eatNum();
135 | nx = x + eatNum();
136 | ny = y + eatNum();
137 | path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
138 | x = nx; y = ny;
139 | break;
140 | // - quadratic bezier
141 | case 'Q':
142 | x1 = eatNum(); y1 = eatNum();
143 | case 'T':
144 | if (activeCmd === 'T') {
145 | x1 = 2 * x - x1;
146 | y1 = 2 * y - y1;
147 | }
148 | nx = eatNum();
149 | ny = eatNum();
150 | path.quadraticCurveTo(x1, y1, nx, ny);
151 | x = nx;
152 | y = ny;
153 | break;
154 | case 'q':
155 | x1 = x + eatNum();
156 | y1 = y + eatNum();
157 | case 't':
158 | if (activeCmd === 't') {
159 | x1 = 2 * x - x1;
160 | y1 = 2 * y - y1;
161 | }
162 | nx = x + eatNum();
163 | ny = y + eatNum();
164 | path.quadraticCurveTo(x1, y1, nx, ny);
165 | x = nx; y = ny;
166 | break;
167 | // - elliptical arc
168 | case 'A':
169 | rx = eatNum();
170 | ry = eatNum();
171 | xar = eatNum() * DEGS_TO_RADS;
172 | laf = eatNum();
173 | sf = eatNum();
174 | nx = eatNum();
175 | ny = eatNum();
176 | if (rx !== ry) {
177 | console.warn("Forcing elliptical arc to be a circular one :(",
178 | rx, ry);
179 | }
180 | // SVG implementation notes does all the math for us! woo!
181 | // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
182 | // step1, using x1 as x1'
183 | x1 = Math.cos(xar) * (x - nx) / 2 + Math.sin(xar) * (y - ny) / 2;
184 | y1 = -Math.sin(xar) * (x - nx) / 2 + Math.cos(xar) * (y - ny) / 2;
185 | // step 2, using x2 as cx'
186 | var norm = Math.sqrt(
187 | (rx*rx * ry*ry - rx*rx * y1*y1 - ry*ry * x1*x1) /
188 | (rx*rx * y1*y1 + ry*ry * x1*x1));
189 | if (laf === sf)
190 | norm = -norm;
191 | x2 = norm * rx * y1 / ry;
192 | y2 = norm * -ry * x1 / rx;
193 | // step 3
194 | cx = Math.cos(xar) * x2 - Math.sin(xar) * y2 + (x + nx) / 2;
195 | cy = Math.sin(xar) * x2 + Math.cos(xar) * y2 + (y + ny) / 2;
196 |
197 | var u = new THREE.Vector2(1, 0),
198 | v = new THREE.Vector2((x1 - x2) / rx,
199 | (y1 - y2) / ry);
200 | var startAng = Math.acos(u.dot(v) / u.length() / v.length());
201 | if (u.x * v.y - u.y * v.x < 0)
202 | startAng = -startAng;
203 |
204 | // we can reuse 'v' from start angle as our 'u' for delta angle
205 | u.x = (-x1 - x2) / rx;
206 | u.y = (-y1 - y2) / ry;
207 |
208 | var deltaAng = Math.acos(v.dot(u) / v.length() / u.length());
209 | // This normalization ends up making our curves fail to triangulate...
210 | if (v.x * u.y - v.y * u.x < 0)
211 | deltaAng = -deltaAng;
212 | if (!sf && deltaAng > 0)
213 | deltaAng -= Math.PI * 2;
214 | if (sf && deltaAng < 0)
215 | deltaAng += Math.PI * 2;
216 |
217 | path.absarc(cx, cy, rx, startAng, startAng + deltaAng, sf);
218 | x = nx;
219 | y = ny;
220 | break;
221 | default:
222 | throw new Error("weird path command: " + activeCmd);
223 | }
224 | if (firstX === null) {
225 | firstX = x;
226 | firstY = y;
227 | }
228 | // just reissue the command
229 | if (canRepeat && nextIsNum())
230 | continue;
231 | activeCmd = pathStr[idx++];
232 | }
233 |
234 | return path;
235 | }
236 |
237 | function applySVGTransform(obj, tstr) {
238 | var idx = tstr.indexOf('('), len = tstr.length,
239 | cmd = tstr.substring(0, idx++);
240 | function eatNum() {
241 | var sidx, c, isFloat = false, s;
242 | // eat delims
243 | while (idx < len) {
244 | c = tstr.charCodeAt(idx);
245 | if (c !== COMMA && c !== SPACE)
246 | break;
247 | idx++;
248 | }
249 | if (c === MINUS)
250 | sidx = idx++;
251 | else
252 | sidx = idx;
253 | // eat number
254 | while (idx < len) {
255 | c = tstr.charCodeAt(idx);
256 | if (DIGIT_0 <= c && c <= DIGIT_9) {
257 | idx++;
258 | continue;
259 | }
260 | else if (c === PERIOD) {
261 | idx++;
262 | isFloat = true;
263 | continue;
264 | }
265 |
266 | s = tstr.substring(sidx, idx);
267 | return isFloat ? parseFloat(s) : parseInt(s);
268 | }
269 |
270 | s = tstr.substring(sidx);
271 | return isFloat ? parseFloat(s) : parseInt(s);
272 | }
273 | switch (cmd) {
274 | case 'translate':
275 | obj.position.x = Math.floor(eatNum() * UNIT_SIZE);
276 | obj.position.y = Math.floor(eatNum() * UNIT_SIZE);
277 | //console.log("translated:", obj.position.x, obj.position.y);
278 | break;
279 | case 'scale':
280 | obj.scale.x = Math.floor(eatNum() * UNIT_SIZE);
281 | obj.scale.y = Math.floor(eatNum() * UNIT_SIZE);
282 | break;
283 | default:
284 | console.warn("don't understand transform", tstr);
285 | break;
286 | }
287 | }
288 |
289 | function wrap_setAttribute(name, value) {
290 | }
291 | function wrap_setAttributeNS(namespace, name, value) {
292 | }
293 |
294 | var extrudeDefaults = {
295 | amount: 1,
296 | bevelEnabled: false,
297 | material: 0,
298 | extrudeMaterial: 0,
299 | };
300 |
301 | function commonSetAttribute(name, value) {
302 | switch (name) {
303 | case 'x':
304 | this.position.x = Math.floor(value * UNIT_SIZE);
305 | break;
306 |
307 | case 'y':
308 | this.position.y = Math.floor(value * UNIT_SIZE);
309 | break;
310 |
311 | case 'class':
312 | this.clazz = value;
313 | break;
314 |
315 | case 'stroke':
316 | case 'fill':
317 | if (typeof(value) !== 'string')
318 | value = value.toString();
319 | this.material.color.setHex(parseInt(value.substring(1), 16));
320 | break;
321 |
322 | case 'transform':
323 | applySVGTransform(this, value);
324 | break;
325 |
326 | case 'd':
327 | var shape = transformSVGPath(value),
328 | geom = shape.extrude(extrudeDefaults);
329 | this.geometry = geom;
330 | this.geometry.boundingSphere = {radius: 3 * UNIT_SIZE};
331 | this.scale.set(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
332 |
333 | break;
334 |
335 | default:
336 | throw new Error("no setter for: " + name);
337 | }
338 | }
339 | function commonSetAttributeNS(namespace, name, value) {
340 | this.setAttribute(name, value);
341 | }
342 |
343 | function Group(parentThing) {
344 | THREE.Object3D.call(this);
345 |
346 | this.d3class = '';
347 |
348 | parentThing.add(this);
349 | };
350 | Group.prototype = new THREE.Object3D();
351 | Group.prototype.constructor = Group;
352 | Group.prototype.d3tag = 'g';
353 | Group.prototype.setAttribute = commonSetAttribute;
354 | Group.prototype.setAttributeNS = commonSetAttributeNS;
355 |
356 | function fabGroup() {
357 | return new Group(this);
358 | }
359 |
360 | function Mesh(parentThing, tag, geometry, material) {
361 | THREE.Mesh.call(this, geometry, material);
362 |
363 | this.d3tag = tag;
364 | this.d3class = '';
365 |
366 | parentThing.add(this);
367 | }
368 | Mesh.prototype = new THREE.Mesh();
369 | Mesh.prototype.constructor = Mesh;
370 | Mesh.prototype.setAttribute = commonSetAttribute;
371 | Mesh.prototype.setAttributeNS = commonSetAttributeNS;
372 |
373 |
374 | const SPHERE_SEGS = 16, SPHERE_RINGS = 16,
375 | DEFAULT_COLOR = 0xcc0000;
376 |
377 | var sharedSphereGeom = null,
378 | sharedCubeGeom = null;
379 |
380 | function fabSphere() {
381 | if (!sharedSphereGeom)
382 | sharedSphereGeom = new THREE.SphereGeometry(
383 | UNIT_SIZE / 2, SPHERE_SEGS, SPHERE_RINGS);
384 | var material = new THREE.MeshLambertMaterial({
385 | color: DEFAULT_COLOR,
386 | });
387 | return new Mesh(this, 'sphere', sharedSphereGeom, material);
388 | }
389 |
390 | function fabCube() {
391 | if (!sharedCubeGeom)
392 | sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
393 | var material = new THREE.MeshLambertMaterial({
394 | color: DEFAULT_COLOR,
395 | });
396 | return new Mesh(this, 'cube', sharedCubeGeom, material);
397 | }
398 |
399 | function fabPath() {
400 | // start with a cube that we will replace with the path once it gets created
401 | if (!sharedCubeGeom)
402 | sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
403 | var material = new THREE.MeshLambertMaterial({
404 | color: DEFAULT_COLOR,
405 | });
406 | return new Mesh(this, 'path', sharedCubeGeom, material);
407 | }
408 |
409 | function Scene() {
410 | THREE.Scene.call(this);
411 | this.renderer = null;
412 | this.camera = null;
413 | this.controls = null;
414 | this._d3_width = null;
415 | this._d3_height = null;
416 | }
417 | Scene.prototype = new THREE.Scene();
418 | Scene.prototype.constructor = Scene;
419 | Scene.prototype._setBounds = function() {
420 | this.renderer.setSize(this._d3_width, this._d3_height);
421 | var aspect = this.camera.aspect;
422 | this.camera.position.set(
423 | this._d3_width * UNIT_SIZE / 2,
424 | this._d3_height * UNIT_SIZE / 2,
425 | Math.max(this._d3_width * UNIT_SIZE / Math.sqrt(2),
426 | this._d3_height * UNIT_SIZE / Math.sqrt(2)));
427 | this.controls.target.set(this.camera.position.x, this.camera.position.y, 0);
428 | console.log("camera:", this.camera.position.x, this.camera.position.y,
429 | this.camera.position.z);
430 |
431 |
432 |
433 | //this.camera.position.z = 1000;
434 | };
435 | Scene.prototype.setAttribute = function(name, value) {
436 | switch (name) {
437 | case 'width':
438 | this._d3_width = value;
439 | if (this._d3_height)
440 | this._setBounds();
441 | break;
442 | case 'height':
443 | this._d3_height = value;
444 | if (this._d3_width)
445 | this._setBounds();
446 | break;
447 | }
448 | };
449 |
450 | function fabVis() {
451 | var camera, scene, controls, renderer;
452 |
453 | // - scene
454 | scene = new Scene();
455 |
456 | // - camera
457 | camera = scene.camera = new THREE.PerspectiveCamera(
458 | 75,
459 | window.innerWidth / window.innerHeight,
460 | 1, 100000);
461 | /*
462 | camera = scene.camera = new THREE.OrthographicCamera(
463 | window.innerWidth / -2, window.innerWidth / 2,
464 | window.innerHeight / 2, window.innerHeight / -2,
465 | 1, 50000);
466 | */
467 | scene.add(camera);
468 |
469 | // - controls
470 | // from misc_camera_trackball.html example
471 | controls = scene.controls = new THREE.TrackballControls(camera);
472 | controls.rotateSpeed = 1.0;
473 | controls.zoomSpeed = 1.2;
474 | controls.panSpeed = 0.8;
475 |
476 | controls.noZoom = false;
477 | controls.noPan = false;
478 |
479 | controls.staticMoving = true;
480 | controls.dynamicDampingFactor = 0.3;
481 |
482 | controls.keys = [65, 83, 68];
483 |
484 | controls.addEventListener('change', render);
485 |
486 | // - light
487 | /*
488 | var pointLight = new THREE.PointLight(0xFFFFFF);
489 | pointLight.position.set(10, 50, 130);
490 | scene.add(pointLight);
491 | */
492 |
493 | var spotlight = new THREE.SpotLight(0xffffff);
494 | spotlight.position.set(-50000, 50000, 100000);
495 | scene.add(spotlight);
496 |
497 | var backlight = new THREE.SpotLight(0x888888);
498 | backlight.position.set(50000, -50000, -100000);
499 | scene.add(backlight);
500 |
501 | /*
502 | var ambientLight = new THREE.AmbientLight(0x888888);
503 | scene.add(ambientLight);
504 | */
505 |
506 | function helperPlanes(maxBound) {
507 | var geom = new THREE.PlaneGeometry(maxBound, maxBound, 4, 4);
508 | for (var i = 0; i < 4; i++) {
509 | var color, cx, cy;
510 | switch (i) {
511 | case 0:
512 | color = 0xff0000;
513 | cx = maxBound / 2;
514 | cy = maxBound / 2;
515 | break;
516 | case 1:
517 | color = 0x00ff00;
518 | cx = maxBound / 2;
519 | cy = -maxBound / 2;
520 | break;
521 | case 2:
522 | color = 0x0000ff;
523 | cx = -maxBound / 2;
524 | cy = -maxBound / 2;
525 | break;
526 | case 3:
527 | color = 0xffff00;
528 | cx = -maxBound / 2;
529 | cy = maxBound / 2;
530 | break;
531 | }
532 | var material = new THREE.MeshLambertMaterial({ color: color });
533 | var mesh = new THREE.Mesh(geom, material);
534 | mesh.position.set(cx, cy, -1);
535 | scene.add(mesh);
536 | }
537 | }
538 | //helperPlanes(UNIT_SIZE * 225);
539 |
540 | // - renderer
541 | renderer = scene.renderer = new THREE.WebGLRenderer({
542 | // too slow...
543 | //antialias: true,
544 | });
545 | this.appendChild( renderer.domElement );
546 |
547 | // - stats
548 | var stats = new Stats();
549 | stats.domElement.style.position = 'absolute';
550 | stats.domElement.style.top = '0px';
551 | stats.domElement.style.zIndex = 100;
552 | this.appendChild( stats.domElement );
553 |
554 | function animate() {
555 | requestAnimationFrame(animate, renderer.domElement);
556 | controls.update();
557 | }
558 |
559 | function render() {
560 | renderer.render(scene, camera);
561 | stats.update();
562 | }
563 |
564 | animate();
565 |
566 | return scene;
567 | };
568 |
569 |
570 | d3.selection.prototype.append3d = function(name) {
571 | var append;
572 | switch (name) {
573 | case 'svg':
574 | append = fabVis;
575 | break;
576 |
577 | case 'g':
578 | append = fabGroup;
579 | break;
580 |
581 | case 'path':
582 | append = fabPath;
583 | break;
584 |
585 | case 'text':
586 | case 'line':
587 | case 'rect':
588 | throw new Error("Did not implement: " + name);
589 | break;
590 |
591 | case 'sphere':
592 | append = fabSphere;
593 | break;
594 | }
595 |
596 | return this.select(append);
597 | };
598 | d3.selection.enter.prototype.append3d = d3.selection.prototype.append3d;
599 |
600 | function select3d_selector(constraint) {
601 | var tagCheck = null, classCheck = null,
602 | idxPeriod = constraint.indexOf('.');
603 | if (idxPeriod === -1) {
604 | tagCheck = constraint;
605 | }
606 | else if (idxPeriod === 0) {
607 | classCheck = constraint.substring(1);
608 | }
609 | else {
610 | tagCheck = constraint.substring(0, idxPeriod);
611 | classCheck = constraint.substring(idxPeriod + 1);
612 | }
613 | return function() {
614 | var results = [];
615 | for (var i = 0; i < this.children.length; i++) {
616 | var kid = this.children[i];
617 | if ((!tagCheck || kid.d3tag === tagCheck) &&
618 | (!classCheck || kid.d3class === classCheck))
619 | results.push(kid);
620 | }
621 | return results;
622 | };
623 | }
624 |
625 | d3.selection.prototype.select3d = function(constraint) {
626 | return this.select(select3d_selector(constraint));
627 | };
628 | d3.selection.prototype.selectAll3d = function(constraint) {
629 | return this.selectAll(select3d_selector(constraint));
630 | };
631 |
632 |
633 | }
634 |
635 | var $d3g = {};
636 | d3threeD($d3g);
637 |
--------------------------------------------------------------------------------