diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000..b093415
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,44 @@
+module.exports = function(grunt) {
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ indoormap: {
+ // potential config here
+ },
+ autoprefixer: {
+ dist: {
+ files: {
+ 'css/app.css': 'css/app.css'
+ }
+ },
+ },
+ sass: {
+ dist: {
+ options: {
+ outputStyle: 'compressed'
+ },
+ files: {
+ 'css/app.css': 'scss/app.scss'
+ }
+ }
+ },
+ watch: {
+ sass: {
+ files: ['scss/*.scss'],
+ tasks: ['sass', 'autoprefixer'],
+ },
+ livereload: {
+ files: ['*.js', 'css/app.css'],
+ options: {
+ livereload: true
+ }
+ }
+ }
+ });
+
+ // load all grunt tasks
+ require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
+
+ grunt.registerTask('build', ['sass']);
+ grunt.registerTask('default', ['build','watch']);
+};
diff --git a/README.md b/README.md
index 2daa421..3943628 100644
--- a/README.md
+++ b/README.md
@@ -22,3 +22,5 @@
- Wireframe of room per floor tagged with name of room
- Highlight room by drawing color semitransparent glowing cube on tob of room floor
- Controller by query param and browser controls
+
+# Ref: http://threejs.org/examples/#webgl_lines_colors
diff --git a/css/app.css b/css/app.css
new file mode 100644
index 0000000..33d21f8
--- /dev/null
+++ b/css/app.css
@@ -0,0 +1 @@
+body,html{width:100%;height:100%;padding:0;margin:0;overflow:hidden;}canvas{width:100%;height:100%;}.debug #svg{position:absolute;top:0;bottom:0;left:0;right:0;width:100%;height:100%;}
\ No newline at end of file
diff --git a/data/neuf04.svg b/data/neuf04.svg
index 59e61d6..f2fbd4c 100644
--- a/data/neuf04.svg
+++ b/data/neuf04.svg
@@ -15,10 +15,10 @@
width="1000"
height="1000"
xml:space="preserve"
- sodipodi:docname="neuf04_simplified_bare.svg">image/svg+xml
\ No newline at end of file
+ inkscape:label="#rect11470" />
diff --git a/data/rooms.json b/data/rooms.json
index 0a8b7e2..d106b5a 100644
--- a/data/rooms.json
+++ b/data/rooms.json
@@ -1 +1,506 @@
-{"rooms":[{"number":"447","pos":{"x":"53.564445","y":"-158.08394"},"size":{"height":"77.731247","width":"77.904381"}},{"number":"450","pos":{"x":"318.01108","y":"-185.0863"},"size":{"height":"69.93187","width":"36.000183"}},{"number":"448","pos":{"x":"354.71475","y":"-185.08415"},"size":{"height":"100.39677","width":"112.169"}},{"number":"423","pos":{"x":"466.89041","y":"-185.09079"},"size":{"height":"101.79505","width":"146.80646"}},{"number":"427","pos":{"x":"665.62469","y":"-230.11064"},"size":{"height":"45.028267","width":"76.190018"}},{"number":"437","pos":{"x":"756.34747","y":"-184.39943"},"size":{"height":"291.53769","width":"35.318886"}},{"number":["428","429"],"pos":{"x":"791.67664","y":"-184.38683"},"size":{"height":"146.09106","width":"117.69923"}},{"number":"430","pos":{"x":"793.05017","y":"-36.899376"},"size":{"height":"68.555847","width":"117.72215"}},{"number":"431","pos":{"x":"793.05017","y":"30.963974"},"size":{"height":"76.865646","width":"118.41465"}},{"number":"440","pos":{"x":"792.3576","y":"107.82961"},"size":{"height":"146.11398","width":"118.41465"}},{"number":"436","pos":{"x":"756.38678","y":"107.17565"},"size":{"height":"294.92099","width":"35.932209"}},{"number":"435","pos":{"x":"792.3576","y":"321.80695"},"size":{"height":"81.020546","width":"117.02967"}},{"number":"444","pos":{"x":"685.02264","y":"474.84579"},"size":{"height":"71.325775","width":"72.710747"}},{"number":"402","pos":{"x":"827.67426","y":"498.39026"},"size":{"height":"44.318932","width":"72.710747"}},{"number":"420","pos":{"x":"758.47394","y":"581.2832"},"size":{"height":"115.85214","width":"69.047668"}},{"number":"411","pos":{"x":"22.717258","y":"580.50562"},"size":{"height":"82.557472","width":"53.380753"}},{"number":"403","pos":{"x":"152.27817","y":"580.30676"},"size":{"height":"38.193451","width":"43.090046"}},{"number":"409","pos":{"x":"195.37247","y":"580.31085"},"size":{"height":"78.540001","width":"49.447254"}},{"number":"412","pos":{"x":"23.009211","y":"546.86469"},"size":{"height":"33.930614","width":"589.30219"}},{"number":"407","pos":{"x":"319.73325","y":"580.29822"},"size":{"height":"117.53546","width":"75.99839"}},{"number":"406","pos":{"x":"395.63904","y":"580.30676"},"size":{"height":"118.00798","width":"70.021324"}},{"number":"405","pos":{"x":"465.17068","y":"580.79633"},"size":{"height":"117.02865","width":"146.89789"}},{"number":"404","pos":{"x":"612.0686","y":"581.7757"},"size":{"height":"115.55968","width":"76.386887"}},{"number":"421","pos":{"x":"688.44983","y":"581.48309"},"size":{"height":"115.65492","width":"70.522224"}},{"number":"419","pos":{"x":"827.03021","y":"543.58331"},"size":{"height":"153.60759","width":"80.791679"}},{"number":"422","pos":{"x":"22.019121","y":"108.75488"},"size":{"height":"32.623585","width":"34.29538"}},{"number":"417","pos":{"x":"22.518375","y":"141.57167"},"size":{"height":"205.65706","width":"105.27682"}},{"number":"414","pos":{"x":"23.008051","y":"401.58087"},"size":{"height":"43.579708","width":"71.490303"}},{"number":"413","pos":{"x":"23.008051","y":"444.6709"},"size":{"height":"102.8285","width":"71.490265"}}]}
\ No newline at end of file
+{
+ "rooms": [
+ {
+ "number": "447",
+ "position": {
+ "x": 32.507306,
+ "y": 106.53646
+ },
+ "dimensions": {
+ "height": "77.731247",
+ "width": "77.904381"
+ }
+ },
+ {
+ "number": "424",
+ "position": {
+ "x": 8.183843,
+ "y": 81.95326000000003
+ },
+ "dimensions": {
+ "height": "102.3144",
+ "width": "24.410036"
+ }
+ },
+ {
+ "number": "424",
+ "position": {
+ "x": 32.420785,
+ "y": 81.60706000000002
+ },
+ "dimensions": {
+ "height": "24.756271",
+ "width": "77.904366"
+ }
+ },
+ {
+ "number": "450",
+ "position": {
+ "x": 296.953941,
+ "y": 79.53410000000002
+ },
+ "dimensions": {
+ "height": "69.93187",
+ "width": "36.000183"
+ }
+ },
+ {
+ "number": "448",
+ "position": {
+ "x": 333.657611,
+ "y": 79.53625000000002
+ },
+ "dimensions": {
+ "height": "100.39677",
+ "width": "112.169"
+ }
+ },
+ {
+ "number": "423",
+ "position": {
+ "x": 445.83327099999997,
+ "y": 79.52961000000002
+ },
+ "dimensions": {
+ "height": "101.79505",
+ "width": "146.80646"
+ }
+ },
+ {
+ "number": "426",
+ "position": {
+ "x": 592.562431,
+ "y": 80.28223000000003
+ },
+ "dimensions": {
+ "height": "66.478394",
+ "width": "142.67001"
+ }
+ },
+ {
+ "number": "426",
+ "position": {
+ "x": 596.241571,
+ "y": 48.68484000000001
+ },
+ "dimensions": {
+ "height": "31.161745",
+ "width": "48.699829"
+ }
+ },
+ {
+ "number": "427",
+ "position": {
+ "x": 644.567551,
+ "y": 34.50976000000003
+ },
+ "dimensions": {
+ "height": "45.028267",
+ "width": "76.190018"
+ }
+ },
+ {
+ "number": "437",
+ "position": {
+ "x": 735.290331,
+ "y": 80.22097000000002
+ },
+ "dimensions": {
+ "height": "291.53769",
+ "width": "35.318886"
+ }
+ },
+ {
+ "number": [
+ "428",
+ "429"
+ ],
+ "position": {
+ "x": 770.619501,
+ "y": 80.23357000000001
+ },
+ "dimensions": {
+ "height": "146.09106",
+ "width": "117.69923"
+ }
+ },
+ {
+ "number": "430",
+ "position": {
+ "x": 771.993031,
+ "y": 227.72102400000003
+ },
+ "dimensions": {
+ "height": "68.555847",
+ "width": "117.72215"
+ }
+ },
+ {
+ "number": "431",
+ "position": {
+ "x": 771.993031,
+ "y": 295.584374
+ },
+ "dimensions": {
+ "height": "76.865646",
+ "width": "118.41465"
+ }
+ },
+ {
+ "number": "440",
+ "position": {
+ "x": 771.300461,
+ "y": 372.45001
+ },
+ "dimensions": {
+ "height": "146.11398",
+ "width": "118.41465"
+ }
+ },
+ {
+ "number": "436",
+ "position": {
+ "x": 735.329641,
+ "y": 371.79605000000004
+ },
+ "dimensions": {
+ "height": "294.92099",
+ "width": "35.932209"
+ }
+ },
+ {
+ "number": "435",
+ "position": {
+ "x": 771.300461,
+ "y": 586.4273499999999
+ },
+ "dimensions": {
+ "height": "81.020546",
+ "width": "117.02967"
+ }
+ },
+ {
+ "number": "444",
+ "position": {
+ "x": 663.965501,
+ "y": 739.4661900000001
+ },
+ "dimensions": {
+ "height": "71.325775",
+ "width": "72.710747"
+ }
+ },
+ {
+ "number": "432",
+ "position": {
+ "x": 947.883721,
+ "y": 661.21558
+ },
+ "dimensions": {
+ "height": "101.79505",
+ "width": "47.781349"
+ }
+ },
+ {
+ "number": "432",
+ "position": {
+ "x": 889.022691,
+ "y": 737.3887400000001
+ },
+ "dimensions": {
+ "height": "75.480667",
+ "width": "60.246052"
+ }
+ },
+ {
+ "number": "402",
+ "position": {
+ "x": 806.617121,
+ "y": 763.01066
+ },
+ "dimensions": {
+ "height": "44.318932",
+ "width": "72.710747"
+ }
+ },
+ {
+ "number": "420",
+ "position": {
+ "x": 737.416801,
+ "y": 845.9036
+ },
+ "dimensions": {
+ "height": "115.85214",
+ "width": "69.047668"
+ }
+ },
+ {
+ "number": "438",
+ "position": {
+ "x": 591.254811,
+ "y": 810.7920300000001
+ },
+ "dimensions": {
+ "height": "35.316593",
+ "width": "214.66977"
+ }
+ },
+ {
+ "number": "438",
+ "position": {
+ "x": 737.368761,
+ "y": 666.75544
+ },
+ "dimensions": {
+ "height": "96.255173",
+ "width": "94.177734"
+ }
+ },
+ {
+ "number": "438",
+ "position": {
+ "x": 831.546501,
+ "y": 742.23609
+ },
+ "dimensions": {
+ "height": "21.466982",
+ "width": "58.168594"
+ }
+ },
+ {
+ "number": "438",
+ "position": {
+ "x": 737.022451,
+ "y": 763.35682
+ },
+ "dimensions": {
+ "height": "47.088917",
+ "width": "69.248306"
+ }
+ },
+ {
+ "number": "410",
+ "position": {
+ "x": 55.278837,
+ "y": 845.12431
+ },
+ "dimensions": {
+ "height": "83.735199",
+ "width": "75.554283"
+ }
+ },
+ {
+ "number": "410",
+ "position": {
+ "x": 1.659669000000001,
+ "y": 928.3727200000001
+ },
+ "dimensions": {
+ "height": "34.078312",
+ "width": "150.62112"
+ }
+ },
+ {
+ "number": "411",
+ "position": {
+ "x": 1.6601190000000017,
+ "y": 845.12602
+ },
+ "dimensions": {
+ "height": "82.557472",
+ "width": "53.380753"
+ }
+ },
+ {
+ "number": "403",
+ "position": {
+ "x": 131.22103099999998,
+ "y": 844.9271600000001
+ },
+ "dimensions": {
+ "height": "38.193451",
+ "width": "43.090046"
+ }
+ },
+ {
+ "number": "409",
+ "position": {
+ "x": 174.315331,
+ "y": 844.93125
+ },
+ "dimensions": {
+ "height": "78.540001",
+ "width": "49.447254"
+ }
+ },
+ {
+ "number": "408",
+ "position": {
+ "x": 152.27645099999998,
+ "y": 923.76225
+ },
+ "dimensions": {
+ "height": "38.193455",
+ "width": "146.40823"
+ }
+ },
+ {
+ "number": "408",
+ "position": {
+ "x": 223.27709099999998,
+ "y": 844.9271
+ },
+ "dimensions": {
+ "height": "78.345589",
+ "width": "75.407585"
+ }
+ },
+ {
+ "number": "412",
+ "position": {
+ "x": 1.9520720000000011,
+ "y": 811.48509
+ },
+ "dimensions": {
+ "height": "33.930614",
+ "width": "589.30219"
+ }
+ },
+ {
+ "number": "407",
+ "position": {
+ "x": 298.676111,
+ "y": 844.91862
+ },
+ "dimensions": {
+ "height": "117.53546",
+ "width": "75.99839"
+ }
+ },
+ {
+ "number": "406",
+ "position": {
+ "x": 374.581901,
+ "y": 844.9271600000001
+ },
+ "dimensions": {
+ "height": "118.00798",
+ "width": "70.021324"
+ }
+ },
+ {
+ "number": "405",
+ "position": {
+ "x": 444.113541,
+ "y": 845.41673
+ },
+ "dimensions": {
+ "height": "117.02865",
+ "width": "146.89789"
+ }
+ },
+ {
+ "number": "404",
+ "position": {
+ "x": 591.0114609999999,
+ "y": 846.3961
+ },
+ "dimensions": {
+ "height": "115.55968",
+ "width": "76.386887"
+ }
+ },
+ {
+ "number": "421",
+ "position": {
+ "x": 667.392691,
+ "y": 846.10349
+ },
+ "dimensions": {
+ "height": "115.65492",
+ "width": "70.522224"
+ }
+ },
+ {
+ "number": "419",
+ "position": {
+ "x": 805.973071,
+ "y": 808.20371
+ },
+ "dimensions": {
+ "height": "153.60759",
+ "width": "80.791679"
+ }
+ },
+ {
+ "number": "422",
+ "position": {
+ "x": 0.961981999999999,
+ "y": 373.37528000000003
+ },
+ "dimensions": {
+ "height": "32.623585",
+ "width": "34.29538"
+ }
+ },
+ {
+ "number": "417",
+ "position": {
+ "x": 1.4612359999999995,
+ "y": 406.19207000000006
+ },
+ "dimensions": {
+ "height": "205.65706",
+ "width": "105.27682"
+ }
+ },
+ {
+ "number": "415",
+ "position": {
+ "x": 1.4612520000000018,
+ "y": 610.86979
+ },
+ "dimensions": {
+ "height": "54.841881",
+ "width": "43.579708"
+ }
+ },
+ {
+ "number": "415",
+ "position": {
+ "x": 44.551297999999996,
+ "y": 633.39411
+ },
+ "dimensions": {
+ "height": "31.338219",
+ "width": "63.1661"
+ }
+ },
+ {
+ "number": "414",
+ "position": {
+ "x": 1.9509119999999989,
+ "y": 666.20127
+ },
+ "dimensions": {
+ "height": "43.579708",
+ "width": "71.490303"
+ }
+ },
+ {
+ "number": "413",
+ "position": {
+ "x": 1.9509119999999989,
+ "y": 709.2913000000001
+ },
+ "dimensions": {
+ "height": "102.8285",
+ "width": "71.490265"
+ }
+ }
+ ],
+ "dimensions": {
+ "width": "1000",
+ "heigh": "1000"
+ }
+}
\ No newline at end of file
diff --git a/index.html b/index.html
index 26f3990..16fc957 100644
--- a/index.html
+++ b/index.html
@@ -4,10 +4,14 @@
Indoor map of Chateau Neuf
-
-
+
+
-
+
+
+
+
+
diff --git a/index.js b/index.js
index e69de29..c167e5f 100644
--- a/index.js
+++ b/index.js
@@ -0,0 +1,69 @@
+var cubes = [];
+var scene = new THREE.Scene();
+
+//var sceneWidth = 1000;
+//var sceneHeight = 1000;
+var sceneWidth = window.innerWidth;
+var sceneHeight = window.innerHeight;
+
+var camera = new THREE.PerspectiveCamera( 75, sceneHeight / sceneWidth, 0.1, 2500 );
+
+var renderer = new THREE.WebGLRenderer({ alpha: true});
+renderer.setSize(sceneWidth, sceneHeight);
+document.body.appendChild( renderer.domElement );
+
+// material
+var green_color = 0x00ff00;
+var red_color = 0xff0000;
+var material = new THREE.MeshBasicMaterial( { color: green_color, wireframe: true } );
+var red_material = new THREE.MeshBasicMaterial( { color: red_color, wireframe: true } );
+
+function render() {
+ var i = 0;
+ requestAnimationFrame(render);
+
+ for(i=0; i -1 || room.number.indexOf("429") > -1) ) {
+ // KAK
+ cube = new THREE.Mesh( geometry, red_material );
+ } else {
+ cube = new THREE.Mesh( geometry, material );
+ }
+
+ cube.position.x = room.position.x;
+ cube.position.y = room.position.y * -1; // FIXME why flipped?
+ cube.name = room.number;
+
+ cubes.push(cube);
+ scene.add( cube );
+ }
+
+ camera.position.z = 1000;
+ camera.position.x = 500;
+ camera.position.y = -500;
+
+ //camera.lookAt(scene.position);
+ //camera.rotation.z = -270 * (Math.PI / 180);
+
+ render();
+}
+
+// load rooms
+$.getJSON("/data/rooms.json", onRoomData);
+
diff --git a/package.json b/package.json
index 2ae83c0..ff29a17 100644
--- a/package.json
+++ b/package.json
@@ -12,5 +12,13 @@
"three": "~0.66.2",
"libxml-to-js": "~0.3.11",
"brfs": "~1.1.1"
+ },
+ "devDependencies": {
+ "node-sass": "latest",
+ "grunt": "latest",
+ "grunt-autoprefixer": "latest",
+ "grunt-contrib-watch": "latest",
+ "grunt-sass": "latest",
+ "matchdep": "latest"
}
}
diff --git a/parse.js b/parse.js
index cc7fd40..f90630b 100644
--- a/parse.js
+++ b/parse.js
@@ -2,7 +2,8 @@
var util = require("util"),
fs = require("fs"),
- parser = require('libxml-to-js');
+ parser = require('libxml-to-js'),
+ utils = require("./utils");
var xml = fs.readFileSync(__dirname + "/data/neuf04.svg", "utf-8");
parser(xml, function (err, result) {
@@ -13,27 +14,42 @@ parser(xml, function (err, result) {
// rooms
var g = result.g.rect;
+ var transform = result.g['@'].transform;
+ console.log(transform);
+ //translate(-21.057139,264.6204)
+ var translateX = -21.057139;
+ var translateY = 264.6204;
var rooms = [];
Object.keys(g).forEach(function(key) {
var r = g[key]['@'];
var number = r.id;
+
// more than one room number per shape?
if(r.id.indexOf(',') > -1) {
number = r.id.split(",");
}
- rooms.push({
+ // more than one shape per room number?
+ if(r.id.indexOf('_') > -1) {
+ number = r.id.split("_")[0];
+ }
+ // TODO adjust position to top left instead of center
+ // ie. find vector from center to top left corner
+ var room = {
number: number,
- pos: {
- x: r.x,
- y: r.y
+ position: {
+ x: parseFloat(r.x) + translateX,
+ y: parseFloat(r.y) + translateY
},
- size: {
+ dimensions: {
height: r.height,
width: r.width,
}
- });
+ };
+ //utils.applySVGTransform(room, transform);
+ console.log(room);
+ rooms.push(room);
});
+ var data = {rooms: rooms, dimensions: { width: width, heigh: height}};
-fs.writeFileSync(__dirname + "/data/rooms.json", JSON.stringify({rooms: rooms}), "utf-8");
-
+ fs.writeFileSync(__dirname + "/data/rooms.json", JSON.stringify(data, null, 4), "utf-8");
});
diff --git a/scss/app.scss b/scss/app.scss
new file mode 100644
index 0000000..4b395a7
--- /dev/null
+++ b/scss/app.scss
@@ -0,0 +1,22 @@
+body,
+html
+{
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ margin: 0;
+ overflow: hidden;
+}
+canvas {
+ width: 100%;
+ height: 100%;
+}
+.debug #svg{
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ width: 100%;
+ height: 100%;
+}
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..db51878
--- /dev/null
+++ b/server.js
@@ -0,0 +1,9 @@
+var express = require('express'),
+ http = require('http');
+
+var app = express()
+ .use(express.logger('dev'))
+ .use("/", express.static(__dirname));
+
+console.log("http://localhost:3000");
+http.createServer(app).listen(3000);
diff --git a/utils.js b/utils.js
new file mode 100644
index 0000000..799f85b
--- /dev/null
+++ b/utils.js
@@ -0,0 +1,289 @@
+/* From https://github.com/asutherland/d3-threeD/blob/master/lib/d3-threeD.js
+ * ...Which might be useful in the future
+ * Ref: http://www.smartjava.org/content/render-geographic-information-3d-threejs-and-d3js
+ */
+const DEGS_TO_RADS = Math.PI / 180,
+ UNIT_SIZE = 100;
+
+const DIGIT_0 = 48, DIGIT_9 = 57, COMMA = 44, SPACE = 32, PERIOD = 46,
+ MINUS = 45;
+function transformSVGPath(pathStr) {
+ 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;
+ activeCmd = pathStr[0];
+ while (idx <= len) {
+ canRepeat = true;
+ switch (activeCmd) {
+ // moveto commands, become lineto's if repeated
+ case 'M':
+ 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':
+ canRepeat = false;
+ if (x !== firstX || y !== firstY)
+ path.lineTo(firstX, firstY);
+ 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;
+ default:
+ throw new Error("weird path command: " + activeCmd);
+ }
+ if (firstX === null) {
+ firstX = x;
+ firstY = y;
+ }
+ // just reissue the command
+ if (canRepeat && nextIsNum())
+ continue;
+ activeCmd = pathStr[idx++];
+ }
+
+ return path;
+}
+
+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());
+ obj.position.y = Math.floor(eatNum());
+ //obj.position.x = Math.floor(eatNum() * UNIT_SIZE);
+ //obj.position.y = Math.floor(eatNum() * UNIT_SIZE);
+ //console.log("translated:", obj.position.x, obj.position.y);
+ 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;
+ }
+}
+
+exports.transformSVGPath = transformSVGPath;
+exports.applySVGTransform = applySVGTransform;