123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
- var transformSVGPathExposed;
- function d3threeD(exports) {
- const DEGS_TO_RADS = Math.PI / 180,
- UNIT_SIZE = 1;
- const DIGIT_0 = 48, DIGIT_9 = 57, COMMA = 44, SPACE = 32, PERIOD = 46,
- MINUS = 45;
- function transformSVGPath(pathStr) {
- var paths = [];
- var path = new THREE.Shape();
- var idx = 1, len = pathStr.length, activeCmd,
- x = 0, y = 0, nx = 0, ny = 0, firstX = null, firstY = null,
- x1 = 0, x2 = 0, y1 = 0, y2 = 0,
- rx = 0, ry = 0, xar = 0, laf = 0, sf = 0, cx, cy;
- function eatNum() {
- var sidx, c, isFloat = false, s;
- // eat delims
- while (idx < len) {
- c = pathStr.charCodeAt(idx);
- if (c !== COMMA && c !== SPACE)
- break;
- idx++;
- }
- if (c === MINUS)
- sidx = idx++;
- else
- sidx = idx;
- // eat number
- while (idx < len) {
- c = pathStr.charCodeAt(idx);
- if (DIGIT_0 <= c && c <= DIGIT_9) {
- idx++;
- continue;
- }
- else if (c === PERIOD) {
- idx++;
- isFloat = true;
- continue;
- }
- s = pathStr.substring(sidx, idx);
- return isFloat ? parseFloat(s) : parseInt(s);
- }
- s = pathStr.substring(sidx);
- return isFloat ? parseFloat(s) : parseInt(s);
- }
- function nextIsNum() {
- var c;
- // do permanently eat any delims...
- while (idx < len) {
- c = pathStr.charCodeAt(idx);
- if (c !== COMMA && c !== SPACE)
- break;
- idx++;
- }
- c = pathStr.charCodeAt(idx);
- return (c === MINUS || (DIGIT_0 <= c && c <= DIGIT_9));
- }
- var canRepeat;
- var enteredSub = false;
- var zSeen = false;
- activeCmd = pathStr[0];
- while (idx <= len) {
- canRepeat = true;
- switch (activeCmd) {
- // moveto commands, become lineto's if repeated
- case 'M':
- enteredSub = false;
- x = eatNum();
- y = eatNum();
- path.moveTo(x, y);
- activeCmd = 'L';
- break;
- case 'm':
- x += eatNum();
- y += eatNum();
- path.moveTo(x, y);
- activeCmd = 'l';
- break;
- case 'Z':
- case 'z':
- // z is a special case. This ends a segment and starts
- // a new path. Since the three.js path is continuous
- // we should start a new path here. This also draws a
- // line from the current location to the start location.
- canRepeat = false;
- if (x !== firstX || y !== firstY)
- path.lineTo(firstX, firstY);
- paths.push(path);
- // reset the elements
- firstX = null;
- firstY = null;
- // avoid x,y being set incorrectly
- enteredSub = true;
- path = new THREE.Shape();
- zSeen = true;
- break;
- // - lines!
- case 'L':
- case 'H':
- case 'V':
- nx = (activeCmd === 'V') ? x : eatNum();
- ny = (activeCmd === 'H') ? y : eatNum();
- path.lineTo(nx, ny);
- x = nx;
- y = ny;
- break;
- case 'l':
- case 'h':
- case 'v':
- nx = (activeCmd === 'v') ? x : (x + eatNum());
- ny = (activeCmd === 'h') ? y : (y + eatNum());
- path.lineTo(nx, ny);
- x = nx;
- y = ny;
- break;
- // - cubic bezier
- case 'C':
- x1 = eatNum(); y1 = eatNum();
- case 'S':
- if (activeCmd === 'S') {
- x1 = 2 * x - x2; y1 = 2 * y - y2;
- }
- x2 = eatNum();
- y2 = eatNum();
- nx = eatNum();
- ny = eatNum();
- path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
- x = nx; y = ny;
- break;
- case 'c':
- x1 = x + eatNum();
- y1 = y + eatNum();
- case 's':
- if (activeCmd === 's') {
- x1 = 2 * x - x2;
- y1 = 2 * y - y2;
- }
- x2 = x + eatNum();
- y2 = y + eatNum();
- nx = x + eatNum();
- ny = y + eatNum();
- path.bezierCurveTo(x1, y1, x2, y2, nx, ny);
- x = nx; y = ny;
- break;
- // - quadratic bezier
- case 'Q':
- x1 = eatNum(); y1 = eatNum();
- case 'T':
- if (activeCmd === 'T') {
- x1 = 2 * x - x1;
- y1 = 2 * y - y1;
- }
- nx = eatNum();
- ny = eatNum();
- path.quadraticCurveTo(x1, y1, nx, ny);
- x = nx;
- y = ny;
- break;
- case 'q':
- x1 = x + eatNum();
- y1 = y + eatNum();
- case 't':
- if (activeCmd === 't') {
- x1 = 2 * x - x1;
- y1 = 2 * y - y1;
- }
- nx = x + eatNum();
- ny = y + eatNum();
- path.quadraticCurveTo(x1, y1, nx, ny);
- x = nx; y = ny;
- break;
- // - elliptical arc
- case 'A':
- rx = eatNum();
- ry = eatNum();
- xar = eatNum() * DEGS_TO_RADS;
- laf = eatNum();
- sf = eatNum();
- nx = eatNum();
- ny = eatNum();
- if (rx !== ry) {
- console.warn("Forcing elliptical arc to be a circular one :(",
- rx, ry);
- }
- // SVG implementation notes does all the math for us! woo!
- // http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
- // step1, using x1 as x1'
- x1 = Math.cos(xar) * (x - nx) / 2 + Math.sin(xar) * (y - ny) / 2;
- y1 = -Math.sin(xar) * (x - nx) / 2 + Math.cos(xar) * (y - ny) / 2;
- // step 2, using x2 as cx'
- var norm = Math.sqrt(
- (rx*rx * ry*ry - rx*rx * y1*y1 - ry*ry * x1*x1) /
- (rx*rx * y1*y1 + ry*ry * x1*x1));
- if (laf === sf)
- norm = -norm;
- x2 = norm * rx * y1 / ry;
- y2 = norm * -ry * x1 / rx;
- // step 3
- cx = Math.cos(xar) * x2 - Math.sin(xar) * y2 + (x + nx) / 2;
- cy = Math.sin(xar) * x2 + Math.cos(xar) * y2 + (y + ny) / 2;
- var u = new THREE.Vector2(1, 0),
- v = new THREE.Vector2((x1 - x2) / rx,
- (y1 - y2) / ry);
- var startAng = Math.acos(u.dot(v) / u.length() / v.length());
- if (u.x * v.y - u.y * v.x < 0)
- startAng = -startAng;
- // we can reuse 'v' from start angle as our 'u' for delta angle
- u.x = (-x1 - x2) / rx;
- u.y = (-y1 - y2) / ry;
- var deltaAng = Math.acos(v.dot(u) / v.length() / u.length());
- // This normalization ends up making our curves fail to triangulate...
- if (v.x * u.y - v.y * u.x < 0)
- deltaAng = -deltaAng;
- if (!sf && deltaAng > 0)
- deltaAng -= Math.PI * 2;
- if (sf && deltaAng < 0)
- deltaAng += Math.PI * 2;
- path.absarc(cx, cy, rx, startAng, startAng + deltaAng, sf);
- x = nx;
- y = ny;
- break;
- case ' ':
- // if it's an empty space, just skip it, and see if we can find a real command
- break;
- default:
- throw new Error("weird path command: " + activeCmd);
- }
- if (firstX === null && !enteredSub) {
- firstX = x;
- firstY = y;
- }
- // just reissue the command
- if (canRepeat && nextIsNum())
- continue;
- activeCmd = pathStr[idx++];
- }
- if (zSeen) {
- return paths;
- } else {
- paths.push(path);
- return paths;
- }
- }
- transformSVGPathExposed = transformSVGPath;
- function applySVGTransform(obj, tstr) {
- var idx = tstr.indexOf('('), len = tstr.length,
- cmd = tstr.substring(0, idx++);
- function eatNum() {
- var sidx, c, isFloat = false, s;
- // eat delims
- while (idx < len) {
- c = tstr.charCodeAt(idx);
- if (c !== COMMA && c !== SPACE)
- break;
- idx++;
- }
- if (c === MINUS)
- sidx = idx++;
- else
- sidx = idx;
- // eat number
- while (idx < len) {
- c = tstr.charCodeAt(idx);
- if (DIGIT_0 <= c && c <= DIGIT_9) {
- idx++;
- continue;
- }
- else if (c === PERIOD) {
- idx++;
- isFloat = true;
- continue;
- }
- s = tstr.substring(sidx, idx);
- return isFloat ? parseFloat(s) : parseInt(s);
- }
- s = tstr.substring(sidx);
- return isFloat ? parseFloat(s) : parseInt(s);
- }
- switch (cmd) {
- case 'translate':
- obj.position.x = Math.floor(eatNum() * UNIT_SIZE);
- obj.position.y = Math.floor(eatNum() * UNIT_SIZE);
- break;
- case 'scale':
- obj.scale.x = Math.floor(eatNum() * UNIT_SIZE);
- obj.scale.y = Math.floor(eatNum() * UNIT_SIZE);
- break;
- default:
- console.warn("don't understand transform", tstr);
- break;
- }
- }
- applySVGTransformExposed = applySVGTransform;
- function wrap_setAttribute(name, value) {
- }
- function wrap_setAttributeNS(namespace, name, value) {
- }
- var extrudeDefaults = {
- amount: 20,
- bevelEnabled: true,
- material: 0,
- extrudeMaterial: 0,
- };
- function commonSetAttribute(name, value) {
- switch (name) {
- case 'x':
- this.position.x = Math.floor(value * UNIT_SIZE);
- break;
- case 'y':
- this.position.y = Math.floor(value * UNIT_SIZE);
- break;
- case 'class':
- this.clazz = value;
- break;
- case 'stroke':
- case 'fill':
- if (typeof(value) !== 'string')
- value = value.toString();
- this.material.color.setHex(parseInt(value.substring(1), 16));
- break;
- case 'transform':
- applySVGTransform(this, value);
- break;
- case 'd':
- var shape = transformSVGPath(value),
- geom = shape.extrude(extrudeDefaults);
- this.geometry = geom;
- this.geometry.boundingSphere = {radius: 3 * UNIT_SIZE};
- this.scale.set(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
- break;
- default:
- throw new Error("no setter for: " + name);
- }
- }
- function commonSetAttributeNS(namespace, name, value) {
- this.setAttribute(name, value);
- }
- function Group(parentThing) {
- THREE.Object3D.call(this);
- this.d3class = '';
- parentThing.add(this);
- };
- Group.prototype = new THREE.Object3D();
- Group.prototype.constructor = Group;
- Group.prototype.d3tag = 'g';
- Group.prototype.setAttribute = commonSetAttribute;
- Group.prototype.setAttributeNS = commonSetAttributeNS;
- function fabGroup() {
- return new Group(this);
- }
- function Mesh(parentThing, tag, geometry, material) {
- THREE.Mesh.call(this, geometry, material);
- this.d3tag = tag;
- this.d3class = '';
- parentThing.add(this);
- }
- Mesh.prototype = new THREE.Mesh();
- Mesh.prototype.constructor = Mesh;
- Mesh.prototype.setAttribute = commonSetAttribute;
- Mesh.prototype.setAttributeNS = commonSetAttributeNS;
- const SPHERE_SEGS = 16, SPHERE_RINGS = 16,
- DEFAULT_COLOR = 0xcc0000;
- var sharedSphereGeom = null,
- sharedCubeGeom = null;
- function fabSphere() {
- if (!sharedSphereGeom)
- sharedSphereGeom = new THREE.SphereGeometry(
- UNIT_SIZE / 2, SPHERE_SEGS, SPHERE_RINGS);
- var material = new THREE.MeshLambertMaterial({
- color: DEFAULT_COLOR,
- });
- return new Mesh(this, 'sphere', sharedSphereGeom, material);
- }
- function fabCube() {
- if (!sharedCubeGeom)
- sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
- var material = new THREE.MeshLambertMaterial({
- color: DEFAULT_COLOR,
- });
- return new Mesh(this, 'cube', sharedCubeGeom, material);
- }
- function fabPath() {
- // start with a cube that we will replace with the path once it gets created
- if (!sharedCubeGeom)
- sharedCubeGeom = new THREE.CubeGeometry(UNIT_SIZE, UNIT_SIZE, UNIT_SIZE);
- var material = new THREE.MeshLambertMaterial({
- color: DEFAULT_COLOR,
- });
- return new Mesh(this, 'path', sharedCubeGeom, material);
- }
- function Scene() {
- THREE.Scene.call(this);
- this.renderer = null;
- this.camera = null;
- this.controls = null;
- this._d3_width = null;
- this._d3_height = null;
- }
- Scene.prototype = new THREE.Scene();
- Scene.prototype.constructor = Scene;
- Scene.prototype._setBounds = function() {
- this.renderer.setSize(this._d3_width, this._d3_height);
- var aspect = this.camera.aspect;
- this.camera.position.set(
- this._d3_width * UNIT_SIZE / 2,
- this._d3_height * UNIT_SIZE / 2,
- Math.max(this._d3_width * UNIT_SIZE / Math.sqrt(2),
- this._d3_height * UNIT_SIZE / Math.sqrt(2)));
- this.controls.target.set(this.camera.position.x, this.camera.position.y, 0);
- console.log("camera:", this.camera.position.x, this.camera.position.y,
- this.camera.position.z);
- //this.camera.position.z = 1000;
- };
- Scene.prototype.setAttribute = function(name, value) {
- switch (name) {
- case 'width':
- this._d3_width = value;
- if (this._d3_height)
- this._setBounds();
- break;
- case 'height':
- this._d3_height = value;
- if (this._d3_width)
- this._setBounds();
- break;
- }
- };
- function fabVis() {
- var camera, scene, controls, renderer;
- // - scene
- scene = new Scene();
- threeJsScene = scene;
- // - camera
- camera = scene.camera = new THREE.PerspectiveCamera(
- 75,
- window.innerWidth / window.innerHeight,
- 1, 100000);
- /*
- camera = scene.camera = new THREE.OrthographicCamera(
- window.innerWidth / -2, window.innerWidth / 2,
- window.innerHeight / 2, window.innerHeight / -2,
- 1, 50000);
- */
- scene.add(camera);
- // - controls
- // from misc_camera_trackball.html example
- controls = scene.controls = new THREE.TrackballControls(camera);
- controls.rotateSpeed = 1.0;
- controls.zoomSpeed = 1.2;
- controls.panSpeed = 0.8;
- controls.noZoom = false;
- controls.noPan = false;
- controls.staticMoving = true;
- controls.dynamicDampingFactor = 0.3;
- controls.keys = [65, 83, 68];
- controls.addEventListener('change', render);
- // - light
- /*
- var pointLight = new THREE.PointLight(0xFFFFFF);
- pointLight.position.set(10, 50, 130);
- scene.add(pointLight);
- */
- var spotlight = new THREE.SpotLight(0xffffff);
- spotlight.position.set(-50000, 50000, 100000);
- scene.add(spotlight);
- var backlight = new THREE.SpotLight(0x888888);
- backlight.position.set(50000, -50000, -100000);
- scene.add(backlight);
- /*
- var ambientLight = new THREE.AmbientLight(0x888888);
- scene.add(ambientLight);
- */
- function helperPlanes(maxBound) {
- var geom = new THREE.PlaneGeometry(maxBound, maxBound, 4, 4);
- for (var i = 0; i < 4; i++) {
- var color, cx, cy;
- switch (i) {
- case 0:
- color = 0xff0000;
- cx = maxBound / 2;
- cy = maxBound / 2;
- break;
- case 1:
- color = 0x00ff00;
- cx = maxBound / 2;
- cy = -maxBound / 2;
- break;
- case 2:
- color = 0x0000ff;
- cx = -maxBound / 2;
- cy = -maxBound / 2;
- break;
- case 3:
- color = 0xffff00;
- cx = -maxBound / 2;
- cy = maxBound / 2;
- break;
- }
- var material = new THREE.MeshLambertMaterial({ color: color });
- var mesh = new THREE.Mesh(geom, material);
- mesh.position.set(cx, cy, -1);
- scene.add(mesh);
- }
- }
- //helperPlanes(UNIT_SIZE * 225);
- // - renderer
- renderer = scene.renderer = new THREE.WebGLRenderer({
- // too slow...
- //antialias: true,
- });
- this.appendChild( renderer.domElement );
- // - stats
- var stats = new Stats();
- stats.domElement.style.position = 'absolute';
- stats.domElement.style.top = '0px';
- stats.domElement.style.zIndex = 100;
- this.appendChild( stats.domElement );
- function animate() {
- requestAnimationFrame(animate, renderer.domElement);
- controls.update();
- }
- function render() {
- renderer.render(scene, camera);
- stats.update();
- }
- animate();
- return scene;
- };
- }
- var $d3g = {};
- d3threeD($d3g);
|