├── .gitignore ├── LICENSE ├── README.md ├── bin ├── json-colors └── ldrawclean ├── package.json ├── src ├── clean-file.js ├── http-get.js ├── ldraw.js ├── load-model.js ├── parse-colors.js ├── parse-line.js └── parse-model.js └── tests ├── assets ├── LDConfig.ldr ├── models │ ├── dirty.ldr │ ├── http-model.ldr │ └── model.ldr ├── p │ └── 3.dat └── parts │ ├── 1.dat │ ├── 3001.dat │ ├── part.dat │ └── s │ └── 2.dat ├── clean-file-test.js ├── ldraw-test.js ├── load-colors-test.js └── parse-model-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | yarn.lock 3 | package-lock.json 4 | .idea 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Jason Rowland 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-ldraw 2 | A Library for loading and parsing a LDraw files. 3 | 4 | LDraw 5 | http://www.ldraw.org 6 | 7 | File Format 1.0.2: 8 | http://www.ldraw.org/article/218.html 9 | 10 | npm install ldraw 11 | 12 | 13 | ## ldraw.loadModel(filename, callback) 14 | 15 | Loads a model from the base path. If it doesn't find it in the file system, 16 | it will attempt to load it from the internets. It looks in the following 17 | places in the following order: 18 | 19 | 1. (base path)/parts 20 | 2. (base path)/p 21 | 3. (base path)/models 22 | 4. www.ldraw.org/library/official/parts/ 23 | 5. www.ldraw.org/library/official/p/ 24 | 25 | ```javascript 26 | var ldraw = require('ldraw')('/path/to/ldraw/files'); 27 | ldraw.loadModel('car.dat', function (err, model) { 28 | model.commands.forEach(function (cmd) { 29 | console.log(cmd); 30 | }); 31 | }); 32 | ``` 33 | 34 | Commands can be one of the following: 35 | 36 | ```javascript 37 | // 1 - subfile 38 | var subfile = { 39 | type: 1, color: 2, 40 | x: 0, y: 0, z: 0, 41 | a: 0, b: 0, c: 0, 42 | d: 0, e: 0, f: 0, 43 | g: 0, h: 0, i: 0, 44 | file: '3001.dat' 45 | } 46 | 47 | // 2 - line 48 | var line = { 49 | type: 2, 50 | color: 2, 51 | x1: 0, y1: 0, z1: 0, // first point 52 | x2: 1, y2: 1, z2: 1 // second point 53 | }; 54 | 55 | // 3 - triangle 56 | var line = { 57 | type: 3, 58 | color: 2, 59 | x1: 0, y1: 0, z1: 0, // first point 60 | x2: 1, y2: 1, z2: 1, // second point 61 | x3: 2, y3: 2, z3: 2 // third point 62 | }; 63 | 64 | // 4 - quad 65 | var line = { 66 | type: 4, 67 | color: 2, 68 | x1: 0, y1: 0, z1: 0, // first point 69 | x2: 1, y2: 1, z2: 1, // second point 70 | x3: 2, y3: 2, z3: 2, // third point 71 | x4: 3, y4: 3, z4: 3 // fourth point 72 | }; 73 | 74 | // 5 - optional line 75 | var line = { 76 | type: 5, 77 | color: 24, 78 | x1: 0, y1: 0, z1: 0, // first point 79 | x2: 1, y2: 1, z2: 1, // second point 80 | x3: 2, y3: 2, z3: 2, // first control point 81 | x4: 3, y4: 3, z4: 3 // second control point 82 | }; 83 | ``` 84 | 85 | ## ldraw.loadColors(callback) 86 | 87 | Loads the colors from the LDConfig.ldr file in the base directory. 88 | 89 | ```javascript 90 | var ldraw = require('ldraw')('/path/to/ldraw/files'); 91 | ldraw.loadColors(function (err, colors) { 92 | console.log(colors[0].NAME); 93 | }); 94 | 95 | var black = { 96 | "NAME": "Black", 97 | "CODE": 0, 98 | "VALUE": "#05131D", 99 | "EDGE": "#595959", 100 | "MATERIAL": "SOLID", 101 | "LEGOID": "26 - Black" 102 | } 103 | assert.equals(black.NAME, ldraw.colors[0].NAME); 104 | assert.equals(black.CODE, ldraw.colors[0].CODE); 105 | assert.equals(black.VALUE, ldraw.colors[0].VALUE); 106 | ``` 107 | 108 | ## ldraw.parseModel(str) 109 | 110 | Given a string, it parses the text and returns a list of commands. loadModel 111 | calls this function. 112 | 113 | ```javascript 114 | var ldraw = require('ldraw')('/path/to/ldraw/files'); 115 | var filename = 'messy.ldr'; 116 | var content = fs.readFileSync(filename, { "encoding": "utf8"}); 117 | var model = parseModel(content); 118 | ``` 119 | 120 | ## ldraw.parseColors(str) 121 | 122 | Given a string, it returns an object of all the colors indexed by their code. 123 | loadColors calls this function 124 | 125 | ```javascript 126 | var ldraw = require('ldraw')('/path/to/ldraw/files'); 127 | var content = fs.readFileSync('...LDConfig.ldr', { "encoding": "utf8"}); 128 | var colors = ldraw.parseColors(content); 129 | assert.equals(colors[0].CODE === 0); 130 | assert.equals(colors[0].NAME === 'Black'); 131 | ``` 132 | 133 | ## ldraw.cleanFile(str) 134 | 135 | This function takes ugly values and if the difference between the number and 136 | an integer is less than a small epsilon, it turns them into the integer. This 137 | is useful for reducing the size of ldraw files. 138 | 139 | ```javascript 140 | var ldraw = require('ldraw')('/path/to/ldraw/files'); 141 | var cmd = '1 0 9.99999904632568359375 -7.99999904632568359375 10 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3623.dat'; 142 | var cleaned = ldraw.cleanFile(cmd); 143 | assert.equal(cleaned, '1 0 10 -8 10 0 0 -1 0 1 0 1 0 0 3623.dat'); 144 | ``` 145 | -------------------------------------------------------------------------------- /bin/json-colors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | 5 | 6 | var parseColors = require('../src/parse-colors'); 7 | var filename = path.resolve(path.join(__dirname, '..', 'ldraw', 'LDConfig.ldr')); 8 | var contents = fs.readFileSync(filename, { "encoding": "utf8"}); 9 | var colors = parseColors(contents); 10 | console.log(JSON.stringify(colors, null, ' ')); 11 | 12 | -------------------------------------------------------------------------------- /bin/ldrawclean: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /* 3 | 4 | Usage: 5 | ldrawclean model.ldr > clean.ldr 6 | 7 | This utility cleans up an LDraw file so it uses integers: 8 | 9 | Before: 10 | 0 Sheep 11 | 0 Author: LEGO Digital Designer 4.3 12 | 1 0 0 -24 10 1 0 0 0 1 0 0 0 1 3004.dat 13 | 1 15 0 -32 0 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3020.dat 14 | 1 0 0 -24 -30.0000019073486328125 1 0 0 0 1 0 0 0 1 3004.dat 15 | ... 16 | 17 | After: 18 | 0 Sheep 19 | 0 Author: LEGO Digital Designer 4.3 20 | 1 0 0 -24 10 1 0 0 0 1 0 0 0 1 3004.dat 21 | 1 15 0 -32 0 0 0 -1 0 1 0 1 0 0 3020.dat 22 | 1 0 0 -24 -30 1 0 0 0 1 0 0 0 1 3004.dat 23 | ... 24 | 25 | */ 26 | 27 | var fs = require('fs'); 28 | var yargs = require('yargs'); 29 | var path = require('path'); 30 | var cleanFile = require('../src/clean-file'); 31 | 32 | var filename = yargs.argv._[0]; 33 | if (! filename) { 34 | console.log('Usage: ldrawclean model.ldr > model_clean.ldr'); 35 | process.exit(-1); 36 | } 37 | 38 | filename = path.resolve(yargs.argv._[0]); 39 | 40 | 41 | contents = fs.readFileSync(filename, { "encoding": "utf8"}); 42 | contents = cleanFile(contents); 43 | console.log(contents); 44 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ldraw", 3 | "version": "0.1.1", 4 | "description": "A Library for parsing an LDraw file", 5 | "main": "src/ldraw.js", 6 | "scripts": { 7 | "test": "node_modules/.bin/mocha tests" 8 | }, 9 | "bin": { 10 | "ldrawclean": "./bin/ldrawclean" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/jsonxr/node-ldraw.git" 15 | }, 16 | "author": "Jason Rowland", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/jsonxr/node-ldraw/issues" 20 | }, 21 | "devDependencies": { 22 | "mocha": "^8.1.3" 23 | }, 24 | "homepage": "https://github.com/jsonxr/node-ldraw", 25 | "dependencies": { 26 | "async": "^1.0.0", 27 | "glob": "^7.1.6", 28 | "yargs": "^16.0.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/clean-file.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Some LDraw programs export ugly LDraw numbers that are like 9.99999994 when 3 | * it should be 10. This will clean up the contents so it will have a more 4 | * compact representation. 5 | * 6 | * @param contents 7 | */ 8 | function clean(contents, EPSILON) { 9 | EPSILON = EPSILON || 0.000001; 10 | function roundLDU(value) { 11 | Math.round(value) 12 | if ((value - Math.round(value)) <= EPSILON || (Math.round(value) - value) <= EPSILON) { 13 | return Math.round(value); 14 | } else { 15 | return value; 16 | } 17 | } 18 | 19 | function cleanup1(params) { 20 | for (var i = 2; i <= 13; i++) { 21 | params[i] = roundLDU(params[i]); 22 | } 23 | } 24 | 25 | var lines = contents.split('\n'); 26 | var out = []; 27 | lines.forEach(function (line) { 28 | var params = line.split(' '); 29 | if (params[0] == 1) { 30 | cleanup1(params) 31 | out.push(params.join(' ')); 32 | } else { 33 | out.push(line); 34 | } 35 | }); 36 | return out.join('\n'); 37 | } 38 | 39 | module.exports = clean; 40 | -------------------------------------------------------------------------------- /src/http-get.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | 3 | function httpGet(url, callback) { 4 | http.get(url, function(res) { 5 | var body = ''; 6 | res.on("data", function(chunk) { 7 | body += chunk; 8 | }); 9 | res.on('end', function () { 10 | if (res.statusCode === 200) { 11 | callback(null, body); 12 | } else { 13 | callback('404 ' + url); 14 | } 15 | }) 16 | }).on('error', function(e) { 17 | callback(e); 18 | }); 19 | } 20 | 21 | module.exports = httpGet; 22 | -------------------------------------------------------------------------------- /src/ldraw.js: -------------------------------------------------------------------------------- 1 | // core 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | // npm 5 | var async = require('async'); 6 | var glob = require('glob'); 7 | // app 8 | var httpGet = require('./http-get'); 9 | 10 | var parseColors = require('./parse-colors'); 11 | var parseModel = require('./parse-model'); 12 | var cleanFile = require('./clean-file'); 13 | 14 | 15 | function Ldraw(dir) { 16 | var ldraw = {}; 17 | ldraw.dir = path.resolve(dir); 18 | ldraw._models = {}; 19 | 20 | // models property 21 | Object.defineProperty(ldraw, 'models', { 22 | get: function() { return ldraw._models; }, 23 | enumerable: true, 24 | configurable: true 25 | }); 26 | 27 | // models property 28 | var colors = []; 29 | Object.defineProperty(ldraw, 'colors', { 30 | get: function() { return colors; }, 31 | enumerable: true, 32 | configurable: true 33 | }); 34 | 35 | 36 | /** 37 | * clean up the filename reference since it can be in 38 | * "/p/", "/parts/", or "/models/" 39 | */ 40 | function _getKeyFromFilename(filename) { 41 | var key = filename; 42 | 43 | // Strip out the root directory 44 | if (key.indexOf(ldraw.dir) >= 0) { 45 | key = key.substr(ldraw.dir.length); 46 | } 47 | // Strip off the directory where it came from 48 | if (key.indexOf('/parts/') >= 0) { 49 | key = key.substr('/parts/'.length); 50 | } else if (key.indexOf('/p/') >= 0) { 51 | key = key.substr('/p/'.length); 52 | } else if (key.indexOf('/models/') >= 0) { 53 | key = key.substr('/models/'.length); 54 | } 55 | 56 | // Normalize the filename to be unix like. 57 | // 58 | // Example: "s\\3001p03s.dat" becomes "s/3001p03s.dat" 59 | key = key.replace('\\', '/'); 60 | 61 | return key; 62 | } 63 | 64 | /** 65 | * Clear the model cache 66 | */ 67 | ldraw.clear = function clear() { 68 | ldraw._models = {}; 69 | colors = []; 70 | }; 71 | 72 | ldraw.loadModelFromFile = function loadModelFromFile(filename, callback) { 73 | filename = filename.toLowerCase(); 74 | filename = filename.replace('\\', path.sep); 75 | 76 | function fileExists(subdir, callback) { 77 | var absoluteFilename = path.resolve(ldraw.dir, subdir, filename); 78 | fs.exists(absoluteFilename, callback) 79 | } 80 | // Return all paths where the file exists 81 | async.filter(['parts', 'p', 'models'], fileExists, function (results) { 82 | // If no paths match, then return empty 83 | if (! results || results.length === 0) { 84 | callback(null, null); 85 | return; 86 | } 87 | // We only care about the first result that matches 88 | var file = path.resolve(ldraw.dir, results[0], filename); 89 | 90 | fs.readFile(file, { "encoding": "utf8"}, function (err, contents) { 91 | var model = null; 92 | if (!err) { 93 | model = ldraw.parseModel(contents); 94 | } 95 | callback(err, model); 96 | }); 97 | }); 98 | }; 99 | 100 | ldraw.loadModelFromHttp = function loadModelFromHttp(filename, prefix, callback) { 101 | filename = filename.toLowerCase(); 102 | filename = filename.replace('\\', path.sep); 103 | 104 | 105 | var url = prefix + 'parts/' + filename; 106 | httpGet(url, function (err, content) { 107 | if (!err) { 108 | var model = ldraw.parseModel(content); 109 | callback(null, model); 110 | return; 111 | } 112 | 113 | // Couldn't find it under parts, look under p 114 | url = prefix + 'p/' + filename; 115 | httpGet(url, function (err, content) { 116 | if (!err) { 117 | var model = ldraw.parseModel(content); 118 | callback(null, model); 119 | } else { 120 | callback(err, null); 121 | } 122 | }); 123 | }); 124 | }; 125 | 126 | /** 127 | * Load the model 128 | */ 129 | ldraw.loadModel = function(filename, options, callback) { 130 | if (typeof options === 'function') { 131 | callback = options; 132 | options = {}; 133 | } 134 | options.recursive = (options.recursive !== undefined) ? options.recursive:false; 135 | options.http = options.http !== undefined ? options.http:'http://www.ldraw.org/library/official/'; 136 | if (options.http && options.http.lastIndexOf('/') === options.http.length) { 137 | options.http = options.http + '/'; 138 | } 139 | 140 | function _load(filename, callback) { 141 | var key = _getKeyFromFilename(filename); 142 | ldraw.loadModelFromFile(filename, function (err, model) { 143 | if (model) { 144 | callback(err, key, model); 145 | } else if (options.http) { 146 | ldraw.loadModelFromHttp(filename, options.http, function (err, model) { 147 | callback(err, key, model); 148 | }); 149 | } else { 150 | callback(null, null, null); 151 | } 152 | }); 153 | } 154 | 155 | _load(filename, function (err, key, model) { 156 | if (model) { 157 | model.key = key; 158 | ldraw._models[key] = model; 159 | ldraw.loadRecursiveModels(model, options, function (err) { 160 | callback(err, model); 161 | }); 162 | } else { 163 | callback(err); 164 | } 165 | }); 166 | }; 167 | 168 | ldraw.loadRecursiveModels = function (model, options, callback) { 169 | if (options && options.recursive) { 170 | var files = []; 171 | model.commands.forEach(function (cmd) { 172 | if (cmd.type === 1) { 173 | if (!ldraw._models[cmd.file]) { 174 | ldraw._models[cmd.file] = {}; 175 | files.push(cmd.file); 176 | } 177 | } 178 | }); 179 | 180 | if (files.length > 0) { 181 | function load(file, callback) { 182 | ldraw.loadModel(file, options, callback); 183 | } 184 | 185 | async.each(files, load, function (err) { 186 | callback(err); 187 | }); 188 | } else { 189 | callback(null); 190 | } 191 | } else { 192 | callback(); 193 | } 194 | }; 195 | 196 | ldraw.loadAll = function loadAll(callback) { 197 | // Load colors from the directory 198 | ldraw.loadColors(function (err) { 199 | if (err) { 200 | callback(err); 201 | return; 202 | } 203 | 204 | // Now load all models from the directory 205 | glob(dir + '/**/*.dat', function (err, files) { 206 | async.each(files, ldraw.loadModel, function (err) { 207 | callback(err); 208 | }) 209 | }); 210 | }); 211 | }; 212 | 213 | ldraw.loadColors = function loadColors(callback) { 214 | var filename = path.resolve(path.join(dir, 'LDConfig.ldr')); 215 | fs.readFile(filename, { "encoding": "utf8"}, function (err, content) { 216 | colors = parseColors(content); 217 | callback(err, colors); 218 | }); 219 | }; 220 | 221 | ldraw.parseModel = parseModel; 222 | ldraw.parseColors = parseColors; 223 | ldraw.cleanFile = cleanFile; 224 | 225 | 226 | return ldraw; 227 | } 228 | 229 | module.exports = Ldraw; 230 | -------------------------------------------------------------------------------- /src/load-model.js: -------------------------------------------------------------------------------- 1 | var async = require('async'); 2 | // app 3 | var parseModel = require('./parse-model'); 4 | var httpGet = require('./http-get'); 5 | 6 | 7 | 8 | function resolveHttp(file, cb) { 9 | httpGet('http://www.ldraw.org/library/official/parts/' + file.toLowerCase(), function (err, content) { 10 | if (err) { 11 | httpGet('http://www.ldraw.org/library/official/p/' + file.toLowerCase(), function (err, content) { 12 | cb(err, content); 13 | }); 14 | } else { 15 | cb(err, content); 16 | } 17 | }); 18 | } 19 | 20 | 21 | function loadModel(content, resolve, callback) { 22 | var models = {}; 23 | 24 | 25 | function _loadModel(content, callback) { 26 | var model = parseModel(content); 27 | var urls = []; 28 | 29 | model.commands.forEach(function (cmd) { 30 | if (cmd.type === 'subfile') { 31 | if (! children[cmd.file]) { 32 | children[cmd.file] = cmd.file; 33 | urls.push(cmd.file); 34 | } 35 | } 36 | }); 37 | 38 | 39 | // For each child in this file, we need to get the content and load that model 40 | async.each(urls, 41 | function (url, callback) { 42 | 43 | resolveHttp(url, function (err, content) { 44 | if (! err) { 45 | _loadModel(content, resolve, function (err, child) { 46 | children[url] = child; 47 | callback(err); 48 | }); 49 | } else { 50 | callback(err); 51 | } 52 | }); 53 | }, 54 | function(err){ 55 | callback(err, model); 56 | } 57 | ); 58 | 59 | 60 | 61 | //httpGet(url, function (err, content) { 62 | //}); 63 | } 64 | 65 | _loadModel(content, resolve, function (err, model) { 66 | model.children = children; 67 | callback(err, model); 68 | }) 69 | } 70 | 71 | 72 | module.exports = loadModel; 73 | -------------------------------------------------------------------------------- /src/parse-colors.js: -------------------------------------------------------------------------------- 1 | var parseLine = require('./parse-line'); 2 | 3 | /* 4 | 5 | Parses the LDConfig.ldr file for the colors: 6 | 7 | Returns an array indexed by the color code. 8 | 9 | var colors = parseColors(LDConfig); 10 | var color = colors[114] = { 11 | NAME: 'Glitter_Trans_Dark_Pink', 12 | CODE: 114, 13 | VALUE: '#DF6695', 14 | EDGE: '#9A2A66', 15 | ALPHA: 128, 16 | MATERIAL: 'GLITTER', 17 | GLITTER_VALUE: '#923978', 18 | FRACTION: '0.17', 19 | VFRACTION: '0.2', 20 | SIZE: 1, 21 | LEGOID: '114 - Tr. Medium Reddish-Violet w. Glitter 2%' 22 | } 23 | 24 | */ 25 | 26 | var state = null; 27 | 28 | function parseColors(LDConfig) { 29 | var colors = {}; 30 | var lines = LDConfig.split('\n'); 31 | 32 | function getColor(params) { 33 | var color = {}; 34 | color.NAME = params[2]; 35 | for (var i = 3; i < params.length; i = i + 2) { 36 | //console.log(i + ': ' + params[i] + '=' + params[i+1]); 37 | if (params[i] && params[i+1]) { 38 | if (params[i - 2] === 'MATERIAL' && params[i] === 'VALUE') { 39 | color[params[i - 1] + '_VALUE'] = params[i+1]; 40 | } else { 41 | color[params[i]] = params[i + 1]; 42 | if (params[i + 1] == parseInt(params[i+1], 10)) { 43 | color[params[i]] = parseInt(params[i+1], 10); 44 | } 45 | } 46 | } else { 47 | // Material must be the last parameter on a line according to spec 48 | color.MATERIAL = params[i]; 49 | } 50 | } 51 | return color; 52 | } 53 | 54 | for (var i = 0; i < lines.length; i++) { 55 | var line = lines[i].trim(); 56 | 57 | // Skip blank lines 58 | if (line === '') { 59 | state = null; 60 | continue; 61 | } 62 | 63 | // Does it have a LEGOID? If so, parse it and add it to color object 64 | // 0 // LEGOID 26 - Black 65 | legoIdIdx = line.indexOf('LEGOID'); 66 | if (legoIdIdx > 0) { 67 | // 0 // LEGOID 26 - Black 68 | legoId = line.substr(legoIdIdx + 6).trim(); 69 | i = i + 1; 70 | line = lines[i].trim(); 71 | } else { 72 | legoId = null; 73 | } 74 | 75 | // Parse the color line 76 | // 0 !COLOUR Black CODE 0 VALUE #05131D EDGE #595959 77 | var params = parseLine(line); 78 | if (params && params[0] == 0 && params[1] == '!COLOUR') { 79 | var color = getColor(params); 80 | if (!color.MATERIAL) { 81 | if (color.ALPHA) { 82 | color.MATERIAL ='TRANSPARENT'; 83 | } else { 84 | color.MATERIAL = 'SOLID'; 85 | } 86 | } 87 | if (legoId) { 88 | color.LEGOID = legoId; 89 | } 90 | legoIdIdx = null; 91 | colors[color.CODE] = color; 92 | } 93 | 94 | } 95 | 96 | return colors; 97 | } 98 | 99 | module.exports = parseColors; 100 | -------------------------------------------------------------------------------- /src/parse-line.js: -------------------------------------------------------------------------------- 1 | function parseLine(line) { 2 | if (line) { 3 | var re = /\s+/; 4 | return line.trim().split(re); 5 | } else { 6 | return null; 7 | } 8 | } 9 | 10 | module.exports = parseLine; 11 | -------------------------------------------------------------------------------- /src/parse-model.js: -------------------------------------------------------------------------------- 1 | var parseLine = require('./parse-line'); 2 | 3 | /* 4 | 5 | var model = { 6 | steps: [ 7 | [0,1], 8 | ], 9 | commands: [ 10 | { type:1, color:16, x:0, y:0, z:0, a:0, b:0, c:0, d:0, e:0, f:0, g:0, h:0, i:0, file:'myfile.dat' }, 11 | {} 12 | ] 13 | } 14 | 15 | 16 | 1 x y z a b c d e f g h i 17 | 18 | color: 19 | 16 is the "main color" or "current color" 20 | 24 is the "edge color" or "complement color" 21 | 22 | 23 | / a d g 0 \ / a b c x \ 24 | | b e h 0 | | d e f y | 25 | | c f i 0 | | g h i z | 26 | \ x y z 1 / \ 0 0 0 1 / 27 | 28 | */ 29 | module.exports = parser; 30 | 31 | function addCommand(model, cmd) { 32 | model.commands.push(cmd); 33 | } 34 | 35 | function parse0(line, params, model) { 36 | if (params[1] === 'STEP') { 37 | model.steps = model.steps || [0]; 38 | model.steps.push(model.commands.length); 39 | } else if (line === '0 BFC CERTIFY CCW') { 40 | // 0 BFC CERTIFY CCW (winding order for Back Face Culling) 41 | model.CCW = true; 42 | } else if (line === '0 BFC CERTIFY CW') { 43 | model.CW = true; 44 | } else if (line.indexOf('0 !HISTORY ') >= 0) { 45 | model.history = model.history || []; 46 | model.history.push(line.substr('0 !HISTORY '.length)); 47 | } else if (line.indexOf('0 Name: ') >= 0) { 48 | model.name = line.substr('0 Name: '.length); 49 | } else if (line.indexOf('0 Author: ') >= 0) { 50 | model.author = line.substr('0 Author: '.length); 51 | } else if (line.indexOf('0 !LDRAW_ORG') >= 0) { 52 | model.type = params[2]; 53 | } else { 54 | model.comments = model.comments || []; 55 | var comment = line.substr(2).trim(); 56 | if (comment) { 57 | model.comments.push(line.substr(2)); 58 | } 59 | } 60 | } 61 | 62 | function parse1(line, params, model) { 63 | // subfile 64 | var cmd = { 65 | type: parseInt(params[0]), 66 | color: parseInt(params[1], 10), 67 | x: parseFloat(params[2]), 68 | y: parseFloat(params[3]), 69 | z: parseFloat(params[4]), 70 | a: parseFloat(params[5]), 71 | b: parseFloat(params[6]), 72 | c: parseFloat(params[7]), 73 | d: parseFloat(params[8]), 74 | e: parseFloat(params[9]), 75 | f: parseFloat(params[10]), 76 | g: parseFloat(params[11]), 77 | h: parseFloat(params[12]), 78 | i: parseFloat(params[13]), 79 | file: params[14] 80 | }; 81 | addCommand(model, cmd); 82 | } 83 | 84 | function parse2(line, params, model) { 85 | // line 86 | // 2 x1 y1 z1 x2 y2 z2 87 | var cmd = { 88 | type: parseInt(params[0]), 89 | color: parseInt(params[1], 10), 90 | x1: parseFloat(params[2]), 91 | y1: parseFloat(params[3]), 92 | z1: parseFloat(params[4]), 93 | x2: parseFloat(params[5]), 94 | y2: parseFloat(params[6]), 95 | z2: parseFloat(params[7]) 96 | }; 97 | addCommand(model, cmd); 98 | } 99 | 100 | function parse3(line, params, model) { 101 | // triangle 102 | // 3 x1 y1 z1 x2 y2 z2 x3 y3 z3 103 | var cmd = { 104 | type: parseInt(params[0]), 105 | color: parseInt(params[1], 10), 106 | x1: parseFloat(params[2]), 107 | y1: parseFloat(params[3]), 108 | z1: parseFloat(params[4]), 109 | x2: parseFloat(params[5]), 110 | y2: parseFloat(params[6]), 111 | z2: parseFloat(params[7]), 112 | x3: parseFloat(params[8]), 113 | y3: parseFloat(params[9]), 114 | z3: parseFloat(params[10]) 115 | }; 116 | addCommand(model, cmd); 117 | } 118 | 119 | function parse4(line, params, model) { 120 | // quad 121 | // 4 x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4 122 | var cmd = { 123 | type: parseInt(params[0]), 124 | color: parseInt(params[1], 10), 125 | x1: parseFloat(params[2]), 126 | y1: parseFloat(params[3]), 127 | z1: parseFloat(params[4]), 128 | x2: parseFloat(params[5]), 129 | y2: parseFloat(params[6]), 130 | z2: parseFloat(params[7]), 131 | x3: parseFloat(params[8]), 132 | y3: parseFloat(params[9]), 133 | z3: parseFloat(params[10]), 134 | x4: parseFloat(params[11]), 135 | y4: parseFloat(params[12]), 136 | z4: parseFloat(params[13]) 137 | }; 138 | addCommand(model, cmd); 139 | } 140 | 141 | function parse5(line, params, model) { 142 | // 5 x1 y1 z1 x2 y2 z2 x3 y3 z3 x4 y4 z4 143 | var cmd = { 144 | type: parseInt(params[0]), 145 | color: parseInt(params[1], 10), 146 | x1: parseFloat(params[2]), 147 | y1: parseFloat(params[3]), 148 | z1: parseFloat(params[4]), 149 | x2: parseFloat(params[5]), 150 | y2: parseFloat(params[6]), 151 | z2: parseFloat(params[7]), 152 | x3: parseFloat(params[8]), 153 | y3: parseFloat(params[9]), 154 | z3: parseFloat(params[10]), 155 | x4: parseFloat(params[11]), 156 | y4: parseFloat(params[12]), 157 | z4: parseFloat(params[13]) 158 | }; 159 | addCommand(model, cmd); 160 | } 161 | 162 | var parser = { 163 | '0': parse0, 164 | '1': parse1, 165 | '2': parse2, 166 | '3': parse3, 167 | '4': parse4, 168 | '5': parse5 169 | }; 170 | 171 | currentStep = 0; 172 | 173 | function parseModel(contents) { 174 | var model = { 175 | commands: [] 176 | }; 177 | var lines = contents.split('\n'); 178 | lines.forEach(function (line) { 179 | line = line.trim(); 180 | if (line === '') { 181 | return; 182 | } 183 | var params = parseLine(line); 184 | var fn = parser[params[0]]; 185 | if (fn) { 186 | fn(line, params, model); 187 | } else { 188 | console.log('error parsing line: ' + line); 189 | } 190 | }); 191 | return model; 192 | }; 193 | 194 | module.exports = parseModel; 195 | -------------------------------------------------------------------------------- /tests/assets/LDConfig.ldr: -------------------------------------------------------------------------------- 1 | 0 LDraw.org Configuration File 2 | 0 Name: LDConfig.ldr 3 | 0 Author: LDraw.org 4 | 0 !LDRAW_ORG Configuration UPDATE 2013-03-08 5 | 6 | 0 // LDraw Solid Colours 7 | 0 // LEGOID 26 - Black 8 | 0 !COLOUR Black CODE 0 VALUE #05131D EDGE #595959 9 | 0 // LEGOID 23 - Bright Blue 10 | 0 !COLOUR Blue CODE 1 VALUE #0055BF EDGE #333333 11 | 0 // LEGOID 28 - Dark Green 12 | 0 !COLOUR Green CODE 2 VALUE #257A3E EDGE #333333 13 | 0 // LEGOID 107 - Bright Bluish Green 14 | 0 !COLOUR Dark_Turquoise CODE 3 VALUE #00838F EDGE #333333 15 | 0 // LEGOID 21 - Bright Red 16 | 0 !COLOUR Red CODE 4 VALUE #C91A09 EDGE #333333 17 | 0 // LEGOID 221 - Bright Purple 18 | 0 !COLOUR Dark_Pink CODE 5 VALUE #C870A0 EDGE #333333 19 | 0 // LEGOID 217 - Brown 20 | 0 !COLOUR Brown CODE 6 VALUE #583927 EDGE #1E1E1E 21 | 0 // LEGOID 2 - Grey 22 | 0 !COLOUR Light_Gray CODE 7 VALUE #9BA19D EDGE #333333 23 | 0 // LEGOID 27 - Dark Grey 24 | 0 !COLOUR Dark_Gray CODE 8 VALUE #6D6E5C EDGE #333333 25 | 0 // LEGOID 45 - Light Blue 26 | 0 !COLOUR Light_Blue CODE 9 VALUE #B4D2E3 EDGE #333333 27 | 0 // LEGOID 37 - Bright Green 28 | 0 !COLOUR Bright_Green CODE 10 VALUE #4B9F4A EDGE #333333 29 | 0 // LEGOID 116 - Medium Bluish Green 30 | 0 !COLOUR Light_Turquoise CODE 11 VALUE #55A5AF EDGE #333333 31 | 0 // LEGOID 4 - Brick Red 32 | 0 !COLOUR Salmon CODE 12 VALUE #F2705E EDGE #333333 33 | 0 // LEGOID 9 - Light Reddish Violet 34 | 0 !COLOUR Pink CODE 13 VALUE #FC97AC EDGE #333333 35 | 0 // LEGOID 24 - Bright Yellow 36 | 0 !COLOUR Yellow CODE 14 VALUE #F2CD37 EDGE #333333 37 | 0 // LEGOID 1 - White 38 | 0 !COLOUR White CODE 15 VALUE #FFFFFF EDGE #333333 39 | 0 // LEGOID 6 - Light Green 40 | 0 !COLOUR Light_Green CODE 17 VALUE #C2DAB8 EDGE #333333 41 | 0 // LEGOID 3 - Light Yellow 42 | 0 !COLOUR Light_Yellow CODE 18 VALUE #FBE696 EDGE #333333 43 | 0 // LEGOID 5 - Brick Yellow 44 | 0 !COLOUR Tan CODE 19 VALUE #E4CD9E EDGE #333333 45 | 0 // LEGOID 39 - Light Bluish Violet 46 | 0 !COLOUR Light_Violet CODE 20 VALUE #C9CAE2 EDGE #333333 47 | 0 // LEGOID 104 - Bright Violet 48 | 0 !COLOUR Purple CODE 22 VALUE #81007B EDGE #333333 49 | 0 // LEGOID 196 - Dark Royal Blue 50 | 0 !COLOUR Dark_Blue_Violet CODE 23 VALUE #2032B0 EDGE #1E1E1E 51 | 0 // LEGOID 106 - Bright Orange 52 | 0 !COLOUR Orange CODE 25 VALUE #FE8A18 EDGE #333333 53 | 0 // LEGOID 124 - Bright Reddish Violet 54 | 0 !COLOUR Magenta CODE 26 VALUE #923978 EDGE #333333 55 | 0 // LEGOID 119 - Bright Yellowish Green 56 | 0 !COLOUR Lime CODE 27 VALUE #BBE90B EDGE #333333 57 | 0 // LEGOID 138 - Sand Yellow 58 | 0 !COLOUR Dark_Tan CODE 28 VALUE #958A73 EDGE #333333 59 | 0 // LEGOID 222 - Light Purple 60 | 0 !COLOUR Bright_Pink CODE 29 VALUE #E4ADC8 EDGE #333333 61 | 0 // LEGOID 324 - Medium Lavender 62 | 0 !COLOUR Medium_Lavender CODE 30 VALUE #AC78BA EDGE #333333 63 | 0 // LEGOID 325 - Lavender 64 | 0 !COLOUR Lavender CODE 31 VALUE #E1D5ED EDGE #333333 65 | 0 // LEGOID 36 - Light Yellowish Orange 66 | 0 !COLOUR Very_Light_Orange CODE 68 VALUE #F3CF9B EDGE #333333 67 | 0 // LEGOID 198 - Bright Reddish Lilac 68 | 0 !COLOUR Light_Purple CODE 69 VALUE #CD6298 EDGE #333333 69 | 0 // LEGOID 192 - Reddish Brown 70 | 0 !COLOUR Reddish_Brown CODE 70 VALUE #582A12 EDGE #595959 71 | 0 // LEGOID 194 - Medium Stone Grey 72 | 0 !COLOUR Light_Bluish_Gray CODE 71 VALUE #A0A5A9 EDGE #333333 73 | 0 // LEGOID 199 - Dark Stone Grey 74 | 0 !COLOUR Dark_Bluish_Gray CODE 72 VALUE #6C6E68 EDGE #333333 75 | 0 // LEGOID 102 - Medium Blue 76 | 0 !COLOUR Medium_Blue CODE 73 VALUE #5C9DD1 EDGE #333333 77 | 0 // LEGOID 29 - Medium Green 78 | 0 !COLOUR Medium_Green CODE 74 VALUE #73DCA1 EDGE #333333 79 | 0 // LEGOID 223 - Light Pink 80 | 0 !COLOUR Light_Pink CODE 77 VALUE #FECCCF EDGE #333333 81 | 0 // LEGOID 283 - Light Nougat 82 | 0 !COLOUR Light_Flesh CODE 78 VALUE #F6D7B3 EDGE #333333 83 | 0 // LEGOID 38 - Dark Orange 84 | 0 !COLOUR Medium_Dark_Flesh CODE 84 VALUE #CC702A EDGE #333333 85 | 0 // LEGOID 268 - Medium Lilac 86 | 0 !COLOUR Dark_Purple CODE 85 VALUE #3F3691 EDGE #1E1E1E 87 | 0 // LEGOID 312 - Medium Nougat 88 | 0 !COLOUR Dark_Flesh CODE 86 VALUE #7C503A EDGE #333333 89 | 0 // LEGOID 195 - Medium Royal Blue 90 | 0 !COLOUR Blue_Violet CODE 89 VALUE #4C61DB EDGE #333333 91 | 0 // LEGOID 18 - Nougat 92 | 0 !COLOUR Flesh CODE 92 VALUE #D09168 EDGE #333333 93 | 0 // LEGOID 100 - Light Red 94 | 0 !COLOUR Light_Salmon CODE 100 VALUE #FEBABD EDGE #333333 95 | 0 // LEGOID 110 - Bright Bluish Violet 96 | 0 !COLOUR Violet CODE 110 VALUE #4354A3 EDGE #333333 97 | 0 // LEGOID 112 - Medium Bluish Violet 98 | 0 !COLOUR Medium_Violet CODE 112 VALUE #6874CA EDGE #333333 99 | 0 // LEGOID 115 - Medium Yellowish Green 100 | 0 !COLOUR Medium_Lime CODE 115 VALUE #C7D23C EDGE #333333 101 | 0 // LEGOID 118 - Light Bluish Green 102 | 0 !COLOUR Aqua CODE 118 VALUE #B3D7D1 EDGE #333333 103 | 0 // LEGOID 120 - Light Yellowish Green 104 | 0 !COLOUR Light_Lime CODE 120 VALUE #D9E4A7 EDGE #333333 105 | 0 // LEGOID 125 - Light Orange 106 | 0 !COLOUR Light_Orange CODE 125 VALUE #F9BA61 EDGE #333333 107 | 0 // LEGOID 208 - Light Stone Grey 108 | 0 !COLOUR Very_Light_Bluish_Gray CODE 151 VALUE #E6E3E0 EDGE #333333 109 | 0 // LEGOID 191 - Flame Yellowish Orange 110 | 0 !COLOUR Bright_Light_Orange CODE 191 VALUE #F8BB3D EDGE #333333 111 | 0 // LEGOID 212 - Light Royal Blue 112 | 0 !COLOUR Bright_Light_Blue CODE 212 VALUE #86C1E1 EDGE #333333 113 | 0 // LEGOID 216 - Rust 114 | 0 !COLOUR Rust CODE 216 VALUE #B31004 EDGE #333333 115 | 0 // LEGOID 226 - Cool Yellow 116 | 0 !COLOUR Bright_Light_Yellow CODE 226 VALUE #FFF03A EDGE #333333 117 | 0 // LEGOID 232 - Dove Blue 118 | 0 !COLOUR Sky_Blue CODE 232 VALUE #56BED6 EDGE #333333 119 | 0 // LEGOID 140 - Earth Blue 120 | 0 !COLOUR Dark_Blue CODE 272 VALUE #0D325B EDGE #1E1E1E 121 | 0 // LEGOID 141 - Earth Green 122 | 0 !COLOUR Dark_Green CODE 288 VALUE #184632 EDGE #595959 123 | 0 // LEGOID 308 - Dark Brown 124 | 0 !COLOUR Dark_Brown CODE 308 VALUE #352100 EDGE #595959 125 | 0 // LEGOID 11 - Pastel Blue 126 | 0 !COLOUR Maersk_Blue CODE 313 VALUE #54A9C8 EDGE #333333 127 | 0 // LEGOID 154 - New Dark Red 128 | 0 !COLOUR Dark_Red CODE 320 VALUE #720E0F EDGE #333333 129 | 0 // LEGOID 321 - Dark Azur 130 | 0 !COLOUR Dark_Azure CODE 321 VALUE #1498D7 EDGE #333333 131 | 0 // LEGOID 322 - Medium Azur 132 | 0 !COLOUR Medium_Azure CODE 322 VALUE #3EC2DD EDGE #333333 133 | 0 // LEGOID 323 - Aqua 134 | 0 !COLOUR Light_Aqua CODE 323 VALUE #BDDCD8 EDGE #333333 135 | 0 // LEGOID 326 - Spring Yellowish Green 136 | 0 !COLOUR Yellowish_Green CODE 326 VALUE #DFEEA5 EDGE #333333 137 | 0 // LEGOID 330 - Olive Green 138 | 0 !COLOUR Olive_Green CODE 330 VALUE #9B9A5A EDGE #333333 139 | 0 // LEGOID 153 - Sand Red 140 | 0 !COLOUR Sand_Red CODE 335 VALUE #D67572 EDGE #333333 141 | 0 // LEGOID 22 - Medium Reddish Violet 142 | 0 !COLOUR Medium_Dark_Pink CODE 351 VALUE #F785B1 EDGE #333333 143 | 0 // LEGOID 25 - Earth Orange 144 | 0 !COLOUR Earth_Orange CODE 366 VALUE #FA9C1C EDGE #333333 145 | 0 // LEGOID 136 - Sand Violet 146 | 0 !COLOUR Sand_Purple CODE 373 VALUE #845E84 EDGE #333333 147 | 0 // LEGOID 151 - Sand Green 148 | 0 !COLOUR Sand_Green CODE 378 VALUE #A0BCAC EDGE #333333 149 | 0 // LEGOID 135 - Sand Blue 150 | 0 !COLOUR Sand_Blue CODE 379 VALUE #597184 EDGE #333333 151 | 0 // LEGOID 12 - Light Orange Brown 152 | 0 !COLOUR Fabuland_Brown CODE 450 VALUE #B67B50 EDGE #333333 153 | 0 // LEGOID 105 - Bright Yellowish Orange 154 | 0 !COLOUR Medium_Orange CODE 462 VALUE #FFA70B EDGE #333333 155 | 0 // LEGOID 38 - Dark Orange 156 | 0 !COLOUR Dark_Orange CODE 484 VALUE #A95500 EDGE #333333 157 | 0 // LEGOID 103 - Light Grey 158 | 0 !COLOUR Very_Light_Gray CODE 503 VALUE #E6E3DA EDGE #333333 159 | 160 | 161 | 0 // LDraw Transparent Colours 162 | 0 // LEGOID 40 - Transparent 163 | 0 !COLOUR Trans_Clear CODE 47 VALUE #FCFCFC EDGE #C3C3C3 ALPHA 128 164 | 0 // LEGOID 111 - Transparent Brown 165 | 0 !COLOUR Trans_Black CODE 40 VALUE #635F52 EDGE #171316 ALPHA 128 166 | 0 // LEGOID 41 - Transparent Red 167 | 0 !COLOUR Trans_Red CODE 36 VALUE #C91A09 EDGE #880000 ALPHA 128 168 | 0 // LEGOID 47 - Transparent Fluorescent Reddish Orange 169 | 0 !COLOUR Trans_Neon_Orange CODE 38 VALUE #FF800D EDGE #BD2400 ALPHA 128 170 | 0 // LEGOID 182 - Trans Bright Orange 171 | 0 !COLOUR Trans_Orange CODE 57 VALUE #F08F1C EDGE #A45C28 ALPHA 128 172 | 0 // LEGOID 157 - Transparent Fluorescent Yellow 173 | 0 !COLOUR Trans_Neon_Yellow CODE 54 VALUE #DAB000 EDGE #C3BA3F ALPHA 128 174 | 0 // LEGOID 44 - Transparent Yellow 175 | 0 !COLOUR Trans_Yellow CODE 46 VALUE #F5CD2F EDGE #8E7400 ALPHA 128 176 | 0 // LEGOID 49 - Transparent Fluorescent Green 177 | 0 !COLOUR Trans_Neon_Green CODE 42 VALUE #C0FF00 EDGE #84C300 ALPHA 128 178 | 0 // LEGOID 311 - Transparent Bright Green 179 | 0 !COLOUR Trans_Bright_Green CODE 35 VALUE #56E646 EDGE #9DA86B ALPHA 128 180 | 0 // LEGOID 48 - Transparent Green 181 | 0 !COLOUR Trans_Green CODE 34 VALUE #237841 EDGE #1E6239 ALPHA 128 182 | 0 // LEGOID 43 - Transparent Blue 183 | 0 !COLOUR Trans_Dark_Blue CODE 33 VALUE #0020A0 EDGE #000064 ALPHA 128 184 | 0 // LEGOID 143 - Transparent Fluorescent Blue 185 | 0 !COLOUR Trans_Medium_Blue CODE 41 VALUE #559AB7 EDGE #196973 ALPHA 128 186 | 0 // LEGOID 42 - Transparent Light Blue 187 | 0 !COLOUR Trans_Light_Blue CODE 43 VALUE #AEE9EF EDGE #72B3B0 ALPHA 128 188 | 0 // LEGOID 229 - Transparent Light Bluish Green 189 | 0 !COLOUR Trans_Very_Light_Blue CODE 39 VALUE #C1DFF0 EDGE #85A3B4 ALPHA 128 190 | 0 // LEGOID 236 - Transparent Bright Reddish Lilac 191 | 0 !COLOUR Trans_Light_Purple CODE 44 VALUE #96709F EDGE #5A3463 ALPHA 128 192 | 0 // LEGOID 126 - Transparent Bright Bluish Violet 193 | 0 !COLOUR Trans_Purple CODE 52 VALUE #A5A5CB EDGE #280025 ALPHA 128 194 | 0 // LEGOID 113 - Transparent Medium Reddish Violet 195 | 0 !COLOUR Trans_Dark_Pink CODE 37 VALUE #DF6695 EDGE #A32A59 ALPHA 128 196 | 0 // LEGOID 230 - Transparent Bright Pink 197 | 0 !COLOUR Trans_Pink CODE 45 VALUE #FC97AC EDGE #A8718C ALPHA 128 198 | 199 | 200 | 0 // LDraw Chrome Colours 201 | 0 // LEGOID 299 - Warm Gold Drum Lacq 202 | 0 !COLOUR Chrome_Gold CODE 334 VALUE #BBA53D EDGE #BBB23D CHROME 203 | 0 // LEGOID 298 - Cool Silver Drum Lacq 204 | 0 !COLOUR Chrome_Silver CODE 383 VALUE #E0E0E0 EDGE #A4A4A4 CHROME 205 | 0 // LEGOID 187 - Metallic Earth Orange 206 | 0 !COLOUR Chrome_Antique_Brass CODE 60 VALUE #645A4C EDGE #281E10 CHROME 207 | 0 !COLOUR Chrome_Black CODE 64 VALUE #1B2A34 EDGE #595959 CHROME 208 | 0 // LEGOID 185 - Metallic Bright Blue 209 | 0 !COLOUR Chrome_Blue CODE 61 VALUE #6C96BF EDGE #202A68 CHROME 210 | 0 // LEGOID 147 - Metallic Dark Green 211 | 0 !COLOUR Chrome_Green CODE 62 VALUE #3CB371 EDGE #007735 CHROME 212 | 0 // LEGOID 147 - Metallic Bright Red 213 | 0 !COLOUR Chrome_Pink CODE 63 VALUE #AA4D8E EDGE #6E1152 CHROME 214 | 215 | 216 | 0 // LDraw Pearl Colours 217 | 0 // LEGOID 183 - Metallic White 218 | 0 !COLOUR Pearl_White CODE 183 VALUE #F2F3F2 EDGE #333333 PEARLESCENT 219 | 0 // LEGOID 150 - Metallic Light Grey 220 | 0 !COLOUR Pearl_Very_Light_Gray CODE 150 VALUE #BBBDBC EDGE #333333 PEARLESCENT 221 | 0 // LEGOID 179 - Silver flip/flop 222 | 0 !COLOUR Pearl_Light_Gray CODE 135 VALUE #9CA3A8 EDGE #333333 PEARLESCENT 223 | 0 // LEGOID 131 - Silver 224 | 0 !COLOUR Flat_Silver CODE 179 VALUE #898788 EDGE #333333 PEARLESCENT 225 | 0 // LEGOID 148 - Metallic Dark Grey 226 | 0 !COLOUR Pearl_Dark_Gray CODE 148 VALUE #575857 EDGE #333333 PEARLESCENT 227 | 0 // LEGOID 145 - Sand Blue Metallic 228 | 0 !COLOUR Metal_Blue CODE 137 VALUE #5677BA EDGE #333333 PEARLESCENT 229 | 0 // LEGOID 127 - Gold 230 | 0 !COLOUR Pearl_Light_Gold CODE 142 VALUE #DCBE61 EDGE #333333 PEARLESCENT 231 | 0 // LEGOID 297 - Warm Gold 232 | 0 !COLOUR Pearl_Gold CODE 297 VALUE #CC9C2B EDGE #333333 PEARLESCENT 233 | 0 // LEGOID 147 - Metallic Sand Yellow 234 | 0 !COLOUR Flat_Dark_Gold CODE 178 VALUE #B4883E EDGE #333333 PEARLESCENT 235 | 0 // LEGOID 139 - Copper 236 | 0 !COLOUR Copper CODE 134 VALUE #964A27 EDGE #333333 PEARLESCENT 237 | 238 | 239 | 0 // LDraw Metallic Colours 240 | 0 // LEGOID 315 - Silver Metallic 241 | 0 !COLOUR Metallic_Silver CODE 80 VALUE #A5A9B4 EDGE #333333 METAL 242 | 0 // LEGOID 200 - Lemon Metallic 243 | 0 !COLOUR Metallic_Green CODE 81 VALUE #899B5F EDGE #333333 METAL 244 | 0 // LEGOID 316 - Titanium Metallic 245 | 0 !COLOUR Metallic_Gold CODE 82 VALUE #DBAC34 EDGE #333333 METAL 246 | 0 // LEGOID 149 - Metallic Black 247 | 0 !COLOUR Metallic_Black CODE 83 VALUE #1A2831 EDGE #333333 METAL 248 | 0 // LEGOID 309 - Metalized Silver 249 | 0 !COLOUR Metallic_Dark_Gray CODE 87 VALUE #6D6E5C EDGE #333333 METAL 250 | 251 | 252 | 0 // LDraw Milky Colours 253 | 0 // LEGOID 20 - Nature 254 | 0 !COLOUR Milky_White CODE 79 VALUE #FFFFFF EDGE #C3C3C3 ALPHA 224 255 | 0 // LEGOID 294 - Phosphorescent Green 256 | 0 !COLOUR Glow_In_Dark_Opaque CODE 21 VALUE #E0FFB0 EDGE #A4C374 ALPHA 250 LUMINANCE 15 257 | 0 // LEGOID 50 - Phosphorescent White 258 | 0 !COLOUR Glow_In_Dark_Trans CODE 294 VALUE #BDC6AD EDGE #818A71 ALPHA 250 LUMINANCE 15 259 | 260 | 261 | 0 // LDraw Glitter Colours 262 | 0 // LEGOID 114 - Tr. Medium Reddish-Violet w. Glitter 2% 263 | 0 !COLOUR Glitter_Trans_Dark_Pink CODE 114 VALUE #DF6695 EDGE #9A2A66 ALPHA 128 MATERIAL GLITTER VALUE #923978 FRACTION 0.17 VFRACTION 0.2 SIZE 1 264 | 0 // LEGOID 117 - Transparent Glitter 265 | 0 !COLOUR Glitter_Trans_Clear CODE 117 VALUE #FFFFFF EDGE #C3C3C3 ALPHA 128 MATERIAL GLITTER VALUE #FFFFFF FRACTION 0.08 VFRACTION 0.1 SIZE 1 266 | 0 // LEGOID 129 - Tr. Bright Bluish Violet w. Glitter 2% 267 | 0 !COLOUR Glitter_Trans_Purple CODE 129 VALUE #640061 EDGE #280025 ALPHA 128 MATERIAL GLITTER VALUE #8C00FF FRACTION 0.3 VFRACTION 0.4 SIZE 1 268 | 269 | 270 | 0 // LDraw Speckle Colours 271 | 0 !COLOUR Speckle_Black_Silver CODE 132 VALUE #000000 EDGE #898788 MATERIAL SPECKLE VALUE #898788 FRACTION 0.4 MINSIZE 1 MAXSIZE 3 272 | 0 // LEGOID 132 - Black Glitter 273 | 0 !COLOUR Speckle_Black_Gold CODE 133 VALUE #000000 EDGE #DBAC34 MATERIAL SPECKLE VALUE #DBAC34 FRACTION 0.4 MINSIZE 1 MAXSIZE 3 274 | 0 !COLOUR Speckle_Black_Copper CODE 75 VALUE #000000 EDGE #AB6038 MATERIAL SPECKLE VALUE #AB6038 FRACTION 0.4 MINSIZE 1 MAXSIZE 3 275 | 0 !COLOUR Speckle_Dark_Bluish_Gray_Silver CODE 76 VALUE #635F61 EDGE #898788 MATERIAL SPECKLE VALUE #898788 FRACTION 0.4 MINSIZE 1 MAXSIZE 3 276 | 277 | 278 | 0 // LDraw Rubber Colours 279 | 0 !COLOUR Rubber_Yellow CODE 65 VALUE #F5CD2F EDGE #333333 RUBBER 280 | 0 !COLOUR Rubber_Trans_Yellow CODE 66 VALUE #CAB000 EDGE #8E7400 ALPHA 128 RUBBER 281 | 0 !COLOUR Rubber_Trans_Clear CODE 67 VALUE #FFFFFF EDGE #C3C3C3 ALPHA 128 RUBBER 282 | 0 !COLOUR Rubber_Black CODE 256 VALUE #212121 EDGE #595959 RUBBER 283 | 0 !COLOUR Rubber_Blue CODE 273 VALUE #0033B2 EDGE #333333 RUBBER 284 | 0 !COLOUR Rubber_Red CODE 324 VALUE #C40026 EDGE #333333 RUBBER 285 | 0 !COLOUR Rubber_Orange CODE 350 VALUE #D06610 EDGE #333333 RUBBER 286 | 0 !COLOUR Rubber_Light_Gray CODE 375 VALUE #C1C2C1 EDGE #333333 RUBBER 287 | 0 !COLOUR Rubber_Dark_Blue CODE 406 VALUE #001D68 EDGE #595959 RUBBER 288 | 0 !COLOUR Rubber_Purple CODE 449 VALUE #81007B EDGE #333333 RUBBER 289 | 0 !COLOUR Rubber_Lime CODE 490 VALUE #D7F000 EDGE #333333 RUBBER 290 | 0 !COLOUR Rubber_Light_Bluish_Gray CODE 496 VALUE #A3A2A4 EDGE #333333 RUBBER 291 | 0 !COLOUR Rubber_Flat_Silver CODE 504 VALUE #898788 EDGE #333333 RUBBER 292 | 0 !COLOUR Rubber_White CODE 511 VALUE #FAFAFA EDGE #333333 RUBBER 293 | 294 | 295 | 0 // LDraw Internal Common Material Colours 296 | 0 !COLOUR Main_Colour CODE 16 VALUE #7F7F7F EDGE #333333 297 | 0 !COLOUR Edge_Colour CODE 24 VALUE #7F7F7F EDGE #333333 298 | 0 // LEGOID 109 - Black IR 299 | 0 !COLOUR Trans_Black_IR_Lens CODE 32 VALUE #000000 EDGE #333333 ALPHA 200 300 | 0 !COLOUR Magnet CODE 493 VALUE #656761 EDGE #595959 METAL 301 | 0 !COLOUR Electric_Contact_Alloy CODE 494 VALUE #D0D0D0 EDGE #333333 METAL 302 | 0 !COLOUR Electric_Contact_Copper CODE 495 VALUE #AE7A59 EDGE #333333 METAL 303 | 0 304 | -------------------------------------------------------------------------------- /tests/assets/models/dirty.ldr: -------------------------------------------------------------------------------- 1 | 0 penguin 2 | 0 Author: LEGO Digital Designer 4.3 3 | 1 0 9.99999904632568359375 -7.99999904632568359375 10 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3623.dat 4 | 1 15 9.99999904632568359375 -31.999996185302734375 30.0000019073486328125 1 0 0 0 1 0 0 0 1 3005.dat 5 | 1 0 10.00000095367431640625 -39.999996185302734375 19.9999980926513671875 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3023.dat 6 | 1 0 10.00000095367431640625 -64 9.54999828338623046875 0 0 1 0 1 0 -1 0 0 47905.dat 7 | 1 0 10 -31.999996185302734375 9.54999828338623046875 0 0 1 0 1 0 -1 0 0 47905.dat 8 | 1 15 28.000003814697265625 -53.999996185302734375 9.99999523162841796875 0 -0.999999940395355224609375 0 0 0 1 -0.999999940395355224609375 0 0 98138.dat 9 | 1 15 -7.99999904632568359375 -53.999996185302734375 10.0000019073486328125 0 1 0 0 0 1.00000011920928955078125 1.00000011920928955078125 0 0 98138.dat 10 | 1 14 10.0000019073486328125 -39.999996185302734375 29.9999980926513671875 -1 0 0 0 1 0 0 0 -1 50746.dat 11 | 1 0 0 -22.0000019073486328125 10.0000019073486328125 0 1 0 1 0 0 0 0 -1 50746.dat 12 | 1 0 20 -22.0000019073486328125 9.99999713897705078125 0 -1.00000011920928955078125 0 -1.00000011920928955078125 0 0 0 0 -1 50746.dat 13 | -------------------------------------------------------------------------------- /tests/assets/models/http-model.ldr: -------------------------------------------------------------------------------- 1 | 1 0 0 0 -90 1 0 0 0 1 0 0 0 1 box5.dat 2 | 3 | -------------------------------------------------------------------------------- /tests/assets/models/model.ldr: -------------------------------------------------------------------------------- 1 | 1 0 0 0 -90 1 0 0 0 1 0 0 0 1 part.dat 2 | 3 | -------------------------------------------------------------------------------- /tests/assets/p/3.dat: -------------------------------------------------------------------------------- 1 | 4 16 40 24 20 36 24 16 -36 24 16 -40 24 20 2 | -------------------------------------------------------------------------------- /tests/assets/parts/1.dat: -------------------------------------------------------------------------------- 1 | 1 16 0 0 0 1 0 0 0 1 0 0 0 1 s\2.dat 2 | -------------------------------------------------------------------------------- /tests/assets/parts/3001.dat: -------------------------------------------------------------------------------- 1 | 0 Brick 2 x 4 2 | 0 Name: 3001.dat 3 | 0 Author: James Jessiman 4 | 0 !LDRAW_ORG Part UPDATE 2004-03 5 | 0 !LICENSE Redistributable under CCAL version 2.0 : see CAreadme.txt 6 | 7 | 0 BFC CERTIFY CCW 8 | 9 | 0 !HISTORY 2002-05-07 [unknown] BFC Certification 10 | 0 !HISTORY 2002-06-11 [PTadmin] Official Update 2002-03 11 | 0 !HISTORY 2004-02-08 [Steffen] used s\3001s01.dat 12 | 0 !HISTORY 2004-09-15 [PTadmin] Official Update 2004-03 13 | 0 !HISTORY 2007-05-07 [PTadmin] Header formatted for Contributor Agreement 14 | 0 !HISTORY 2008-07-01 [PTadmin] Official Update 2008-01 15 | 16 | 1 16 0 0 0 1 0 0 0 1 0 0 0 1 s\3001s01.dat 17 | 4 16 -40 0 -20 -40 24 -20 40 24 -20 40 0 -20 18 | 4 16 40 0 20 40 24 20 -40 24 20 -40 0 20 19 | 0 20 | -------------------------------------------------------------------------------- /tests/assets/parts/part.dat: -------------------------------------------------------------------------------- 1 | 2 | 1 16 0 0 0 1 0 0 0 1 0 0 0 1 1.dat 3 | 1 16 0 0 0 1 0 0 0 1 0 0 0 1 s\2.dat 4 | 5 | -------------------------------------------------------------------------------- /tests/assets/parts/s/2.dat: -------------------------------------------------------------------------------- 1 | 1 16 20 4 0 1 0 0 0 -5 0 0 0 1 3.dat 2 | -------------------------------------------------------------------------------- /tests/clean-file-test.js: -------------------------------------------------------------------------------- 1 | //http://www.ldraw.org/library/official/parts/3001.dat 2 | var assert = require('assert'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var cleanFile = require('../src/clean-file'); 6 | var parseModel = require('../src/parse-model'); 7 | 8 | describe("clean-ldraw", function () { 9 | 10 | it("should round models to whole numbers when they are within an EPSILON OF 0.000001", function (done) { 11 | var filename = path.resolve(path.join(__dirname, 'assets', 'models', 'dirty.ldr')); 12 | var contents = fs.readFileSync(filename, { "encoding": "utf8"}); 13 | var model = parseModel(cleanFile(contents)); 14 | assert(model); 15 | // 1 0 9.99999904632568359375 -7.99999904632568359375 10 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3623.dat 16 | var cmd = model.commands[0]; 17 | assert.strictEqual(cmd.x, 10); 18 | assert.strictEqual(cmd.y, -8); 19 | assert.strictEqual(cmd.e, 1); 20 | done(); 21 | }); 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /tests/ldraw-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var Ldraw = require('../src/ldraw'); 5 | 6 | describe("ldraw", function () { 7 | it("should load all models", function (done) { 8 | var ldrawPath = path.resolve(__dirname, 'assets'); 9 | var ldraw = Ldraw(ldrawPath); 10 | 11 | ldraw.loadAll(function (err, callback) { 12 | assert(ldraw.models); 13 | assert(Object.keys(ldraw.models).length); 14 | assert.notStrictEqual(Object.keys(ldraw.models).length, 0); 15 | done(); 16 | }); 17 | }); 18 | 19 | it("should load single file from filesystem", function (done) { 20 | var ldrawPath = path.resolve(__dirname, 'assets'); 21 | var ldraw = Ldraw(ldrawPath); 22 | ldraw.loadModel('part.dat', function (err, model) { 23 | assert(! err); 24 | assert(model); 25 | assert(ldraw.models['part.dat']); 26 | assert(!ldraw.models['1.dat']); 27 | assert(!ldraw.models['s/2.dat']); 28 | assert(!ldraw.models['3.dat']); 29 | done(); 30 | }); 31 | }); 32 | 33 | it("should recursively load from filesystem", function (done) { 34 | var ldrawPath = path.resolve(__dirname, 'assets'); 35 | var ldraw = Ldraw(ldrawPath); 36 | ldraw.loadModel('model.ldr', { recursive:true }, function (err, model) { 37 | assert(ldraw.models['part.dat']); 38 | assert(ldraw.models['1.dat']); 39 | assert(ldraw.models['s/2.dat']); 40 | assert(ldraw.models['3.dat']); 41 | done(); 42 | }); 43 | }); 44 | 45 | it("should return error if it doesn't exist on filesystem", function (done) { 46 | var ldrawPath = path.resolve(__dirname, 'assets'); 47 | var ldraw = Ldraw(ldrawPath); 48 | ldraw.loadModel('bogus.dat', { http:null }, function (err, model) { 49 | assert(! err); 50 | assert(! model); 51 | done(); 52 | }); 53 | }); 54 | 55 | it("should get file from web if it doesn't exist on filesystem", function (done) { 56 | this.timeout(10000) 57 | var ldrawPath = path.resolve(__dirname, 'assets'); 58 | var ldraw = Ldraw(ldrawPath); 59 | ldraw.loadModel('http-model.ldr', { recursive:true }, function (err) { 60 | assert(! err); 61 | assert(ldraw.models['box5.dat']); 62 | done(); 63 | }); 64 | }); 65 | 66 | it("should load colors", function (done) { 67 | this.timeout(10000); 68 | var ldrawPath = path.resolve(__dirname, 'assets'); 69 | var ldraw = Ldraw(ldrawPath); 70 | ldraw.loadColors(function (err, colors) { 71 | assert(colors); 72 | assert(ldraw.colors); 73 | var black = colors[0]; 74 | assert(black); 75 | assert.strictEqual(black.NAME, 'Black'); 76 | assert.strictEqual(black.CODE, 0); 77 | assert.strictEqual(black.VALUE, '#05131D'); 78 | assert.strictEqual(black.MATERIAL, 'SOLID'); 79 | assert.strictEqual(black.LEGOID, '26 - Black'); 80 | done(); 81 | }); 82 | }); 83 | 84 | it("should round models to whole numbers when they are within an EPSILON OF 0.000001", function (done) { 85 | var ldrawPath = path.resolve(__dirname, 'assets'); 86 | var ldraw = Ldraw(ldrawPath); 87 | 88 | assert(ldraw.cleanFile); 89 | assert.strictEqual(typeof ldraw.cleanFile, 'function'); 90 | 91 | var cmd = '1 0 9.99999904632568359375 -7.99999904632568359375 10 0 0 -1 0 0.999999940395355224609375 0 1 0 0 3623.dat'; 92 | var cleaned = ldraw.cleanFile(cmd); 93 | assert.strictEqual(cleaned, '1 0 10 -8 10 0 0 -1 0 1 0 1 0 0 3623.dat'); 94 | 95 | done(); 96 | }); 97 | 98 | it("should expose parseModel on ldraw object", function (done) { 99 | var ldrawPath = path.resolve(__dirname, 'assets'); 100 | var ldraw = Ldraw(ldrawPath); 101 | assert(ldraw.parseModel); 102 | assert.strictEqual(typeof ldraw.parseModel, 'function'); 103 | done(); 104 | }); 105 | 106 | it("should expose parseColors on ldraw object", function (done) { 107 | var ldrawPath = path.resolve(__dirname, 'assets'); 108 | var ldraw = Ldraw(ldrawPath); 109 | assert(ldraw.parseColors); 110 | assert.strictEqual(typeof ldraw.parseColors, 'function'); 111 | done(); 112 | }); 113 | 114 | 115 | }); 116 | -------------------------------------------------------------------------------- /tests/load-colors-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var parseColors = require('../src/parse-colors'); 5 | 6 | 7 | 8 | /* 9 | 10 | 0 // LDraw Solid Colours 11 | 0 // LEGOID 26 - Black 12 | 0 !COLOUR Black CODE 0 VALUE #05131D EDGE #595959 13 | 14 | 15 | 0 // LDraw Transparent Colours 16 | 0 // LEGOID 40 - Transparent 17 | 0 !COLOUR Trans_Clear CODE 47 VALUE #FCFCFC EDGE #C3C3C3 ALPHA 128 18 | 19 | 20 | 0 // LEGOID 294 - Phosphorescent Green 21 | 0 !COLOUR Glow_In_Dark_Opaque CODE 21 VALUE #E0FFB0 EDGE #A4C374 ALPHA 250 LUMINANCE 15 22 | 23 | 24 | 0 // LDraw Glitter Colours 25 | 0 // LEGOID 114 - Tr. Medium Reddish-Violet w. Glitter 2% 26 | 0 !COLOUR Glitter_Trans_Dark_Pink CODE 114 VALUE #DF6695 EDGE #9A2A66 ALPHA 128 MATERIAL GLITTER VALUE #923978 FRACTION 0.17 VFRACTION 0.2 SIZE 1 27 | 28 | 29 | 0 // LDraw Speckle Colours 30 | 0 !COLOUR Speckle_Black_Silver CODE 132 VALUE #000000 EDGE #898788 MATERIAL SPECKLE VALUE #898788 FRACTION 0.4 MINSIZE 1 MAXSIZE 3 31 | 32 | */ 33 | 34 | 35 | describe("parse-colors", function () { 36 | it("should parse colors", function (done) { 37 | var filename = path.resolve(path.join(__dirname, 'assets', 'LDConfig.ldr')); 38 | var contents = fs.readFileSync(filename, { "encoding": "utf8"}); 39 | var colors = parseColors(contents); 40 | assert(colors); 41 | var black = colors[0]; 42 | assert.strictEqual(black.CODE, 0); 43 | assert.strictEqual(black.LEGOID, '26 - Black'); 44 | assert.strictEqual(black.VALUE, '#05131D'); 45 | assert.strictEqual(black.EDGE, '#595959'); 46 | // Check Glitter 47 | var glitter = colors[114]; 48 | assert(glitter); 49 | assert.strictEqual(glitter.LEGOID, '114 - Tr. Medium Reddish-Violet w. Glitter 2%'); 50 | assert.strictEqual(glitter.CODE, 114); 51 | assert.strictEqual(glitter.VALUE, '#DF6695'); 52 | assert.strictEqual(glitter.EDGE, '#9A2A66'); 53 | assert.strictEqual(glitter.GLITTER_VALUE, '#923978'); 54 | assert.strictEqual(glitter.FRACTION, '0.17'); 55 | assert.strictEqual(glitter.VFRACTION, '0.2'); 56 | assert.strictEqual(glitter.SIZE, 1); 57 | assert.strictEqual(glitter.ALPHA, 128); 58 | var speckle = colors[132]; 59 | assert.strictEqual(speckle.SPECKLE_VALUE, '#898788'); 60 | done(); 61 | }) 62 | }); 63 | -------------------------------------------------------------------------------- /tests/parse-model-test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var parseModel = require('../src/parse-model'); 5 | 6 | describe("parse-model", function () { 7 | it("should parse model", function (done) { 8 | var filename = path.resolve(path.join(__dirname, 'assets', 'parts', '3001.dat')); 9 | var contents = fs.readFileSync(filename, { "encoding": "utf8"}); 10 | var model = parseModel(contents); 11 | assert.strictEqual(model.name,'3001.dat') 12 | 13 | // 1 16 0 0 0 1 0 0 0 1 0 0 0 1 s\3001s01.dat 14 | var subfile = model.commands[0]; 15 | assert.strictEqual(subfile.type,1); 16 | assert.strictEqual(subfile.color, 16); 17 | assert.strictEqual(subfile.x, 0); 18 | assert.strictEqual(subfile.y, 0); 19 | assert.strictEqual(subfile.z, 0); 20 | assert.strictEqual(subfile.a, 1); 21 | assert.strictEqual(subfile.b, 0); 22 | assert.strictEqual(subfile.c, 0); 23 | assert.strictEqual(subfile.d, 0); 24 | assert.strictEqual(subfile.e, 1); 25 | assert.strictEqual(subfile.f, 0); 26 | assert.strictEqual(subfile.g, 0); 27 | assert.strictEqual(subfile.h, 0); 28 | assert.strictEqual(subfile.i, 1); 29 | assert.strictEqual(subfile.file, 's\\3001s01.dat'); 30 | 31 | // 4 16 -40 0 -20 -40 24 -20 40 24 -20 40 0 -20 32 | var quad = model.commands[1]; 33 | assert.strictEqual(quad.type, 4); 34 | assert.strictEqual(quad.color, 16); 35 | assert.strictEqual(quad.x1, -40); 36 | assert.strictEqual(quad.y1, 0); 37 | assert.strictEqual(quad.z1, -20); 38 | assert.strictEqual(quad.x2, -40); 39 | assert.strictEqual(quad.y2, 24); 40 | assert.strictEqual(quad.z2, -20); 41 | assert.strictEqual(quad.x3, 40); 42 | assert.strictEqual(quad.y3, 24); 43 | assert.strictEqual(quad.z3, -20); 44 | assert.strictEqual(quad.x4, 40); 45 | assert.strictEqual(quad.y4, 0); 46 | assert.strictEqual(quad.z4, -20); 47 | //assert(quad.x == 0) 48 | 49 | assert(model.commands[2].type === 4); 50 | done(); 51 | }); 52 | 53 | }); 54 | --------------------------------------------------------------------------------