├── index.js ├── .gitignore ├── assets ├── logo.png ├── wifi.png ├── 3dline.png ├── scatter.png ├── border-top.png ├── policecar.png ├── policehat.png ├── border-bottom.png ├── border-left.png ├── border-right.png ├── policestation.png ├── circle.svg ├── heatLinesRoad.json └── loader │ ├── MTLLoader.js │ ├── OBJLoader2.js │ └── LoaderSupport.js ├── src ├── amap.js ├── AMapModel.js ├── AMapView.js └── AMapCoordSys.js ├── webpack.config.js ├── package.json ├── LICENSE ├── examples ├── index.2.html ├── index.4.html ├── index.3.html ├── amap3d-bus-line.html └── index.1.html ├── README.md ├── dist ├── echarts-amap.min.js └── echarts-amap.min.js.map └── index.html /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/echarts-amap.min.js') 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | assets/model/ 4 | npm-debug.log 5 | -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/logo.png -------------------------------------------------------------------------------- /assets/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/wifi.png -------------------------------------------------------------------------------- /assets/3dline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/3dline.png -------------------------------------------------------------------------------- /assets/scatter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/scatter.png -------------------------------------------------------------------------------- /assets/border-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/border-top.png -------------------------------------------------------------------------------- /assets/policecar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/policecar.png -------------------------------------------------------------------------------- /assets/policehat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/policehat.png -------------------------------------------------------------------------------- /assets/border-bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/border-bottom.png -------------------------------------------------------------------------------- /assets/border-left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/border-left.png -------------------------------------------------------------------------------- /assets/border-right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/border-right.png -------------------------------------------------------------------------------- /assets/policestation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dfEric/echarts-amap/HEAD/assets/policestation.png -------------------------------------------------------------------------------- /src/amap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * BMap component extension 3 | */ 4 | require('echarts').registerCoordinateSystem( 5 | 'amap', require('./AMapCoordSys') 6 | ); 7 | require('./AMapModel'); 8 | require('./AMapView'); 9 | 10 | // Action 11 | require('echarts').registerAction({ 12 | type: 'amapRoam', 13 | event: 'amapRoam', 14 | update: 'updateLayout' 15 | }, function (payload, ecModel) { 16 | ecModel.eachComponent('amap', function (aMapModel) { 17 | var amap = aMapModel.getAMap(); 18 | var center = amap.getCenter(); 19 | aMapModel.setCenterAndZoom([center.lng, center.lat], amap.getZoom()); 20 | }); 21 | }); 22 | 23 | module.exports = { 24 | version: process.env.VERSION 25 | }; 26 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack'); 2 | var packagejson = require('./package.json'); 3 | var PROD = process.argv.indexOf('-p') >= 0; 4 | 5 | console.log('building package version ' + packagejson.version) 6 | module.exports = { 7 | entry: { 8 | 'amap': __dirname + '/src/amap.js', 9 | }, 10 | output: { 11 | libraryTarget: 'umd', 12 | library: ['echarts', '[name]'], 13 | path: __dirname + '/dist', 14 | filename: PROD ? 'echarts-[name].min.js' : 'echarts-[name].js' 15 | }, 16 | externals: { 17 | 'echarts': 'echarts' 18 | }, 19 | devtool: PROD ? '#source-map' : '#eval-source-map', 20 | plugins: [ 21 | new webpack.DefinePlugin({ 22 | 'process.env.VERSION': JSON.stringify(packagejson.version) 23 | }) 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /src/AMapModel.js: -------------------------------------------------------------------------------- 1 | function v2Equal(a, b) { 2 | return a && b && a[0] === b[0] && a[1] === b[1]; 3 | } 4 | 5 | module.exports = require('echarts').extendComponentModel({ 6 | type: 'amap', 7 | 8 | getAMap: function () { 9 | // __amap is injected when creating AMapCoordSys 10 | return this.__amap; 11 | }, 12 | 13 | getLayer: function() { 14 | // __layer is injected when creating AMapCoordSys 15 | return this.__layer; 16 | }, 17 | 18 | getMapOptions: function() { 19 | return this.__options; 20 | }, 21 | 22 | setCenterAndZoom: function (center, zoom) { 23 | this.option.center = center; 24 | this.option.zoom = zoom; 25 | }, 26 | centerOrZoomChanged: function (center, zoom) { 27 | var option = this.option; 28 | return !(v2Equal(center, option.center) && zoom === option.zoom); 29 | }, 30 | 31 | defaultOption: { 32 | center: [116.397475,39.908695], 33 | zoom: 4, 34 | } 35 | }); 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "echarts-amap", 3 | "version": "1.0.0-rc.6", 4 | "description": "\u0016an echarts extension to support AMap(http://lbs.amap.com/)", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "webpack-dev-server --open --hot", 8 | "build": "webpack -p", 9 | "test": "echo \"Error: no test specified\" && exit 1" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/billy-poon/echarts-amap.git" 14 | }, 15 | "keywords": [ 16 | "echarts", 17 | "amap", 18 | "alimap", 19 | "autonavi" 20 | ], 21 | "author": "Billy Poon", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/billy-poon/echarts-amap/issues" 25 | }, 26 | "homepage": "https://github.com/billy-poon/echarts-amap#readme", 27 | "devDependencies": { 28 | "webpack": "^2.2.1", 29 | "webpack-dev-server": "^2.4.1" 30 | }, 31 | "dependencies": { 32 | "echarts": "^4.0.4" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 billy-poon 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 | -------------------------------------------------------------------------------- /assets/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /assets/heatLinesRoad.json: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | "河南中路-泗泾路-东南角", 4 | "河南中路-昭通路-西南角", 5 | "河南中路-福州路-东南角", 6 | "河南中路-汉口路-南向中间隔离带", 7 | "河南中路-九江路西-南角", 8 | "河南中路-天津路-西北角", 9 | "河南中路-宁波路-西南角", 10 | "河南中路-北京东路(河南路桥口)-西南角" 11 | ], 12 | [ 13 | "延安东路-江西中路-东北角", 14 | "江西中路-广东路-西南角", 15 | "江西中路-福州路-东北角", 16 | "江西中路-福州路-西北角", 17 | "江西中路-江西中路222号-对面(劳动局信访办)", 18 | "江西中路-九江路-东南角", 19 | "江西中路-滇池路-东南角", 20 | "江西中路-宁波路-东南角" 21 | ], 22 | [ 23 | "四川中路-广东路-东南角", 24 | "四川中路-福州路-东南角", 25 | "四川中路-汉口路-东北角", 26 | "四川中路-九江路-西南角", 27 | "南京东路-四川中路-东北角", 28 | "四川中路-滇池路-西北角", 29 | "北京东路-四川中路-西北角", 30 | "四川中路-四川中路590号-门口" 31 | ], 32 | [ 33 | "中山东一路-广东路-东侧", 34 | "中山东一路-气象广场-北侧", 35 | "中山东一路-福州路-东侧", 36 | "中山东一路-汉口路-东侧", 37 | "中山东一路-九江路-东侧", 38 | "中山东一路-九江路-西北角", 39 | "中山东一路-陈毅广场-南侧", 40 | "中山东一路-南京东路-东侧", 41 | "中山东一路-滇池路-东侧", 42 | "中山东一路-陈毅广场-北侧", 43 | "中山东一路-北京东路-东北角", 44 | "中山东一路-延安东路(水文站)-东侧" 45 | ], 46 | [ 47 | "中山东一路-北京东路-东北角", 48 | "北京东路-圆明园路-西南角", 49 | "北京东路-虎丘路-东北角", 50 | "北京东路-江西中路-东北角", 51 | "北京东路-江西中路-东北角", 52 | "河南中路-北京东路(河南路桥口)-西南角" 53 | ], 54 | [ 55 | "中山东一路-滇池路-东侧", 56 | "圆明园路-滇池路-东北角", 57 | "中山东一路-滇池路-东侧", 58 | "江西中路-滇池路-东南角", 59 | "河南中路-天津路-西北角" 60 | ], 61 | [ 62 | "中山东一路-南京东路-东侧", 63 | "南京东路-四川中路-东北角", 64 | "南京东路-江西中路-东北角" 65 | ], 66 | [ 67 | "中山东一路-九江路-东侧", 68 | "中山东一路-九江路-西北角", 69 | "四川中路-九江路-西南角", 70 | "江西中路-九江路-东南角", 71 | "河南中路-九江路西-南角" 72 | ], 73 | [ 74 | "中山东一路-汉口路-东侧", 75 | "四川中路-汉口路-东北角", 76 | "江西中路-汉口路-西北角", 77 | "汉口路-193号对面", 78 | "河南中路-汉口路-南向中间隔离带" 79 | ], 80 | [ 81 | "中山东一路-福州路-东侧", 82 | "四川中路-福州路-东南角", 83 | "江西中路-福州路-西北角", 84 | "江西中路-福州路-东北角", 85 | "福州路-185号-北侧", 86 | "河南中路-福州路-东南角" 87 | ], 88 | ["中山东一路-广东路-东侧", "四川中路-广东路-东南角", "江西中路-广东路-西南角"] 89 | ] 90 | -------------------------------------------------------------------------------- /examples/index.2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Echarts plugin to support AMap 7 | 8 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /examples/index.4.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 点是否在多边形内 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 73 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECharts Extension AMap 2 | 3 | An echarts extension to support AMap(http://lbs.amap.com/), Ported from the offical echarts `extension-bmap` 4 | 5 | > https://github.com/ecomfe/echarts/tree/master/extension/bmap 6 | 7 | ## Install 8 | 9 | ```bash 10 | npm install -S echarts-amap 11 | ``` 12 | 13 | ## Get Started 14 | 15 | **Using Script Tag** 16 | 17 | ```html 18 | 19 | 20 | 21 | 22 | ECharts AMap Test 23 | 30 | 31 | 32 |
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 77 | 78 | 79 | ``` 80 | 81 | **Using Webpack** 82 | 83 | ```javascript 84 | var echarts = require('echarts') 85 | require('echarts-amap') 86 | 87 | var echart = echarts.init(document.getElementById('map')) 88 | echart.setOption({ 89 | ... // see the example above 90 | }) 91 | ``` 92 | 93 | ###20180417更新日志 94 | 95 | >支持高德地图3D地图,可以实现更多炫酷效果,部分截图如下: 96 | 97 | 98 | ![map3d-scatter](./assets/scatter.png) 99 | 100 | ![map3d-scatter](./assets/3dline.png) 101 | 102 | -------------------------------------------------------------------------------- /src/AMapView.js: -------------------------------------------------------------------------------- 1 | /* 2 | * this function bollowed from: 3 | * https://github.com/Leaflet/Leaflet/blob/master/src/core/Util.js 4 | */ 5 | function throttle(fn, time, context) { 6 | var lock, args, wrapperFn, later; 7 | 8 | later = function () { 9 | // reset lock and call if queued 10 | lock = false; 11 | if (args) { 12 | wrapperFn.apply(context, args); 13 | args = false; 14 | } 15 | }; 16 | 17 | wrapperFn = function () { 18 | if (lock) { 19 | // called too soon, queue to call later 20 | args = arguments; 21 | 22 | } else { 23 | // call and lock until later 24 | fn.apply(context, arguments); 25 | setTimeout(later, time); 26 | lock = true; 27 | } 28 | }; 29 | 30 | return wrapperFn; 31 | } 32 | 33 | var echarts = require('echarts'); 34 | 35 | module.exports = require('echarts').extendComponentView({ 36 | type: 'amap', 37 | render: function (aMapModel, ecModel, api) { 38 | var rendering = true; 39 | 40 | var amap = aMapModel.getAMap(); 41 | var viewportRoot = api.getZr().painter.getViewportRoot(); 42 | var coordSys = aMapModel.coordinateSystem; 43 | var moveHandler = function (e) { 44 | if (rendering) { 45 | return; 46 | } 47 | var offsetEl = viewportRoot.parentNode.parentNode.parentNode; 48 | var mapOffset = [ 49 | -parseInt(offsetEl.style.left, 10) || 0, 50 | -parseInt(offsetEl.style.top, 10) || 0 51 | ]; 52 | viewportRoot.style.left = mapOffset[0] + 'px'; 53 | viewportRoot.style.top = mapOffset[1] + 'px'; 54 | 55 | coordSys.setMapOffset(mapOffset); 56 | aMapModel.__mapOffset = mapOffset; 57 | 58 | api.dispatchAction({ 59 | type: 'amapRoam' 60 | }); 61 | }; 62 | 63 | function zoomEndHandler() { 64 | if (rendering) { 65 | return; 66 | } 67 | api.dispatchAction({ 68 | type: 'amapRoam' 69 | }); 70 | } 71 | 72 | function resizeHandler(e) { 73 | echarts.getInstanceByDom(api.getDom()).resize(); 74 | moveHandler.call(this, e) 75 | } 76 | 77 | var throttledResizeHandler = throttle(resizeHandler, 300, amap); 78 | 79 | amap.off('movestart', this._oldMoveHandler); 80 | amap.off('zoomend', this._oldZoomEndHandler); 81 | amap.off('moveend', this._oldZoomEndHandler); 82 | amap.off('complete', this._oldZoomEndHandler); 83 | aMapModel.get('resizeEnable') && amap.off('resize', this._oldResizeHandler); 84 | 85 | amap.on('movestart', moveHandler); 86 | amap.on('zoomend', zoomEndHandler); 87 | amap.on('moveend', zoomEndHandler); 88 | amap.on('complete', zoomEndHandler); 89 | aMapModel.get('resizeEnable') && amap.on('resize', throttledResizeHandler); 90 | 91 | this._oldMoveHandler = moveHandler; 92 | this._oldZoomEndHandler = zoomEndHandler; 93 | this._oldResizeHandler = throttledResizeHandler; 94 | 95 | // var roam = aMapModel.get('roam'); 96 | // if (roam && roam !== 'scale') { 97 | // amap.enableDragging(); 98 | // } 99 | // else { 100 | // amap.disableDragging(); 101 | // } 102 | // if (roam && roam !== 'move') { 103 | // amap.enableScrollWheelZoom(); 104 | // amap.enableDoubleClickZoom(); 105 | // amap.enablePinchToZoom(); 106 | // } 107 | // else { 108 | // amap.disableScrollWheelZoom(); 109 | // amap.disableDoubleClickZoom(); 110 | // amap.disablePinchToZoom(); 111 | // } 112 | 113 | rendering = false; 114 | } 115 | }); 116 | -------------------------------------------------------------------------------- /examples/index.3.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 自定义图层 18 | 19 | 20 | 21 |
22 | 23 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/AMapCoordSys.js: -------------------------------------------------------------------------------- 1 | var echarts = require('echarts'); 2 | 3 | function AMapCoordSys(amap, api) { 4 | this._amap = amap; 5 | this.dimensions = ['lng', 'lat']; 6 | this._mapOffset = [0, 0]; 7 | 8 | this._api = api; 9 | } 10 | 11 | AMapCoordSys.prototype.dimensions = ['lng', 'lat']; 12 | 13 | AMapCoordSys.prototype.setZoom = function (zoom) { 14 | this._zoom = zoom; 15 | }; 16 | 17 | AMapCoordSys.prototype.setCenter = function (center) { 18 | this._center = this._amap.lnglatToPixel(center);//, 10) 19 | }; 20 | 21 | AMapCoordSys.prototype.setMapOffset = function (mapOffset) { 22 | this._mapOffset = mapOffset; 23 | }; 24 | 25 | AMapCoordSys.prototype.getAMap = function () { 26 | return this._amap; 27 | }; 28 | 29 | AMapCoordSys.prototype.dataToPoint = function (data) { 30 | var point = new AMap.LngLat(data[0], data[1]); 31 | var px = this._amap.lngLatToContainer(point);//, this._zoom); 32 | var mapOffset = this._mapOffset; 33 | return [px.x - mapOffset[0], px.y - mapOffset[1]]; 34 | }; 35 | 36 | AMapCoordSys.prototype.pointToData = function (pt) { 37 | var mapOffset = this._mapOffset; 38 | var pt = this._amap.containerToLngLat({ 39 | x: pt[0] + mapOffset[0], 40 | y: pt[1] + mapOffset[1] 41 | }); 42 | return [pt.lng, pt.lat]; 43 | }; 44 | 45 | AMapCoordSys.prototype.getViewRect = function () { 46 | var api = this._api; 47 | return new echarts.graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight()); 48 | }; 49 | 50 | AMapCoordSys.prototype.getRoamTransform = function () { 51 | return echarts.matrix.create(); 52 | }; 53 | 54 | var Overlay; 55 | 56 | // For deciding which dimensions to use when creating list data 57 | AMapCoordSys.dimensions = AMapCoordSys.prototype.dimensions; 58 | 59 | AMapCoordSys.create = function (ecModel, api) { 60 | var amapCoordSys; 61 | var root = api.getDom(); 62 | 63 | // TODO Dispose 64 | ecModel.eachComponent('amap', function (amapModel) { 65 | var viewportRoot = api.getZr().painter.getViewportRoot(); 66 | if (typeof AMap === 'undefined') { 67 | throw new Error('AMap api is not loaded'); 68 | } 69 | 70 | if (amapCoordSys) { 71 | throw new Error('Only one amap component can exist'); 72 | } 73 | if (!amapModel.__amap) { 74 | // Not support IE8 75 | var amapRoot = root.querySelector('.ec-extension-amap'); 76 | if (amapRoot) { 77 | // Reset viewport left and top, which will be changed 78 | // in moving handler in AMapView 79 | viewportRoot.style.left = '0px'; 80 | viewportRoot.style.top = '0px'; 81 | root.removeChild(amapRoot); 82 | } 83 | amapRoot = document.createElement('div'); 84 | amapRoot.style.cssText = 'width:100%;height:100%'; 85 | // Not support IE8 86 | amapRoot.classList.add('ec-extension-amap'); 87 | root.appendChild(amapRoot); 88 | 89 | var options = amapModel.get() || {}; 90 | options = amapModel.__options = echarts.util.clone(options); 91 | var amap = amapModel.__amap = new AMap.Map(amapRoot, options); 92 | 93 | var layer = amapModel.__layer = new AMap.CustomLayer(viewportRoot); 94 | layer.setMap(amap); 95 | } 96 | var amap = amapModel.getAMap(); 97 | var layer = amapModel.getLayer(); 98 | layer.hide(); 99 | 100 | var zoom = amap.getZoom(); 101 | var center = amap.getCenter(); 102 | 103 | amapCoordSys = new AMapCoordSys(amap, api); 104 | amapCoordSys.setMapOffset(amapModel.__mapOffset || [0, 0]); 105 | amapCoordSys.setZoom(zoom); 106 | amapCoordSys.setCenter([center.lng, center.lat]); 107 | 108 | amapModel.coordinateSystem = amapCoordSys; 109 | layer.show(); 110 | }); 111 | 112 | ecModel.eachSeries(function (seriesModel) { 113 | if (seriesModel.get('coordinateSystem') === 'amap') { 114 | seriesModel.coordinateSystem = amapCoordSys; 115 | } 116 | }); 117 | }; 118 | 119 | module.exports = AMapCoordSys; 120 | -------------------------------------------------------------------------------- /dist/echarts-amap.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("echarts")):"function"==typeof define&&define.amd?define(["echarts"],e):"object"==typeof exports?exports.amap=e(require("echarts")):(t.echarts=t.echarts||{},t.echarts.amap=e(t.echarts))}(this,function(t){return function(t){function e(n){if(o[n])return o[n].exports;var a=o[n]={i:n,l:!1,exports:{}};return t[n].call(a.exports,a,a.exports,e),a.l=!0,a.exports}var o={};return e.m=t,e.c=o,e.i=function(t){return t},e.d=function(t,o,n){e.o(t,o)||Object.defineProperty(t,o,{configurable:!1,enumerable:!0,get:n})},e.n=function(t){var o=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(o,"a",o),o},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=4)}([function(e,o){e.exports=t},function(t,e,o){function n(t,e){this._amap=t,this.dimensions=["lng","lat"],this._mapOffset=[0,0],this._api=e}var a=o(0);n.prototype.dimensions=["lng","lat"],n.prototype.setZoom=function(t){this._zoom=t},n.prototype.setCenter=function(t){this._center=this._amap.lnglatToPixel(t)},n.prototype.setMapOffset=function(t){this._mapOffset=t},n.prototype.getAMap=function(){return this._amap},n.prototype.dataToPoint=function(t){var e=new AMap.LngLat(t[0],t[1]),o=this._amap.lngLatToContainer(e),n=this._mapOffset;return[o.x-n[0],o.y-n[1]]},n.prototype.pointToData=function(t){var e=this._mapOffset,t=this._amap.containerToLngLat({x:t[0]+e[0],y:t[1]+e[1]});return[t.lng,t.lat]},n.prototype.getViewRect=function(){var t=this._api;return new a.graphic.BoundingRect(0,0,t.getWidth(),t.getHeight())},n.prototype.getRoamTransform=function(){return a.matrix.create()};n.dimensions=n.prototype.dimensions,n.create=function(t,e){var o,r=e.getDom();t.eachComponent("amap",function(t){var i=e.getZr().painter.getViewportRoot();if("undefined"==typeof AMap)throw new Error("AMap api is not loaded");if(o)throw new Error("Only one amap component can exist");if(!t.__amap){var p=r.querySelector(".ec-extension-amap");p&&(i.style.left="0px",i.style.top="0px",r.removeChild(p)),p=document.createElement("div"),p.style.cssText="width:100%;height:100%",p.classList.add("ec-extension-amap"),r.appendChild(p);var s=t.get()||{};s=t.__options=a.util.clone(s);var c=t.__amap=new AMap.Map(p,s),f=t.__layer=new AMap.CustomLayer(i);f.setMap(c)}var c=t.getAMap(),f=t.getLayer();f.hide();var m=c.getZoom(),u=c.getCenter();o=new n(c,e),o.setMapOffset(t.__mapOffset||[0,0]),o.setZoom(m),o.setCenter([u.lng,u.lat]),t.coordinateSystem=o,f.show()}),t.eachSeries(function(t){"amap"===t.get("coordinateSystem")&&(t.coordinateSystem=o)})},t.exports=n},function(t,e,o){function n(t,e){return t&&e&&t[0]===e[0]&&t[1]===e[1]}t.exports=o(0).extendComponentModel({type:"amap",getAMap:function(){return this.__amap},getLayer:function(){return this.__layer},getMapOptions:function(){return this.__options},setCenterAndZoom:function(t,e){this.option.center=t,this.option.zoom=e},centerOrZoomChanged:function(t,e){var o=this.option;return!(n(t,o.center)&&e===o.zoom)},defaultOption:{center:[116.397475,39.908695],zoom:4}})},function(t,e,o){function n(t,e,o){var n,a,r,i;return i=function(){n=!1,a&&(r.apply(o,a),a=!1)},r=function(){n?a=arguments:(t.apply(o,arguments),setTimeout(i,e),n=!0)}}var a=o(0);t.exports=o(0).extendComponentView({type:"amap",render:function(t,e,o){function r(){p||o.dispatchAction({type:"amapRoam"})}function i(t){a.getInstanceByDom(o.getDom()).resize(),m.call(this,t)}var p=!0,s=t.getAMap(),c=o.getZr().painter.getViewportRoot(),f=t.coordinateSystem,m=function(e){if(!p){var n=c.parentNode.parentNode.parentNode,a=[-parseInt(n.style.left,10)||0,-parseInt(n.style.top,10)||0];c.style.left=a[0]+"px",c.style.top=a[1]+"px",f.setMapOffset(a),t.__mapOffset=a,o.dispatchAction({type:"amapRoam"})}},u=n(i,300,s);s.off("movestart",this._oldMoveHandler),s.off("zoomend",this._oldZoomEndHandler),s.off("moveend",this._oldZoomEndHandler),s.off("complete",this._oldZoomEndHandler),t.get("resizeEnable")&&s.off("resize",this._oldResizeHandler),s.on("movestart",m),s.on("zoomend",r),s.on("moveend",r),s.on("complete",r),t.get("resizeEnable")&&s.on("resize",u),this._oldMoveHandler=m,this._oldZoomEndHandler=r,this._oldResizeHandler=u,p=!1}})},function(t,e,o){o(0).registerCoordinateSystem("amap",o(1)),o(2),o(3),o(0).registerAction({type:"amapRoam",event:"amapRoam",update:"updateLayout"},function(t,e){e.eachComponent("amap",function(t){var e=t.getAMap(),o=e.getCenter();t.setCenterAndZoom([o.lng,o.lat],e.getZoom())})}),t.exports={version:"1.0.0-rc.6"}}])}); 2 | //# sourceMappingURL=echarts-amap.min.js.map -------------------------------------------------------------------------------- /examples/amap3d-bus-line.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Echarts plugin to support AMap 7 | 8 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /assets/loader/MTLLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads a Wavefront .mtl file specifying materials 3 | * 4 | * @author angelxuanchang 5 | */ 6 | 7 | THREE.MTLLoader = function ( manager ) { 8 | 9 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 10 | 11 | }; 12 | 13 | THREE.MTLLoader.prototype = { 14 | 15 | constructor: THREE.MTLLoader, 16 | 17 | /** 18 | * Loads and parses a MTL asset from a URL. 19 | * 20 | * @param {String} url - URL to the MTL file. 21 | * @param {Function} [onLoad] - Callback invoked with the loaded object. 22 | * @param {Function} [onProgress] - Callback for download progress. 23 | * @param {Function} [onError] - Callback for download errors. 24 | * 25 | * @see setPath setTexturePath 26 | * 27 | * @note In order for relative texture references to resolve correctly 28 | * you must call setPath and/or setTexturePath explicitly prior to load. 29 | */ 30 | load: function ( url, onLoad, onProgress, onError ) { 31 | 32 | var scope = this; 33 | 34 | var loader = new THREE.FileLoader( this.manager ); 35 | loader.setPath( this.path ); 36 | loader.load( url, function ( text ) { 37 | 38 | onLoad( scope.parse( text ) ); 39 | 40 | }, onProgress, onError ); 41 | 42 | }, 43 | 44 | /** 45 | * Set base path for resolving references. 46 | * If set this path will be prepended to each loaded and found reference. 47 | * 48 | * @see setTexturePath 49 | * @param {String} path 50 | * 51 | * @example 52 | * mtlLoader.setPath( 'assets/obj/' ); 53 | * mtlLoader.load( 'my.mtl', ... ); 54 | */ 55 | setPath: function ( path ) { 56 | 57 | this.path = path; 58 | 59 | }, 60 | 61 | /** 62 | * Set base path for resolving texture references. 63 | * If set this path will be prepended found texture reference. 64 | * If not set and setPath is, it will be used as texture base path. 65 | * 66 | * @see setPath 67 | * @param {String} path 68 | * 69 | * @example 70 | * mtlLoader.setPath( 'assets/obj/' ); 71 | * mtlLoader.setTexturePath( 'assets/textures/' ); 72 | * mtlLoader.load( 'my.mtl', ... ); 73 | */ 74 | setTexturePath: function ( path ) { 75 | 76 | this.texturePath = path; 77 | 78 | }, 79 | 80 | setBaseUrl: function ( path ) { 81 | 82 | console.warn( 'THREE.MTLLoader: .setBaseUrl() is deprecated. Use .setTexturePath( path ) for texture path or .setPath( path ) for general base path instead.' ); 83 | 84 | this.setTexturePath( path ); 85 | 86 | }, 87 | 88 | setCrossOrigin: function ( value ) { 89 | 90 | this.crossOrigin = value; 91 | 92 | }, 93 | 94 | setMaterialOptions: function ( value ) { 95 | 96 | this.materialOptions = value; 97 | 98 | }, 99 | 100 | /** 101 | * Parses a MTL file. 102 | * 103 | * @param {String} text - Content of MTL file 104 | * @return {THREE.MTLLoader.MaterialCreator} 105 | * 106 | * @see setPath setTexturePath 107 | * 108 | * @note In order for relative texture references to resolve correctly 109 | * you must call setPath and/or setTexturePath explicitly prior to parse. 110 | */ 111 | parse: function ( text ) { 112 | 113 | var lines = text.split( '\n' ); 114 | var info = {}; 115 | var delimiter_pattern = /\s+/; 116 | var materialsInfo = {}; 117 | 118 | for ( var i = 0; i < lines.length; i ++ ) { 119 | 120 | var line = lines[ i ]; 121 | line = line.trim(); 122 | 123 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 124 | 125 | // Blank line or comment ignore 126 | continue; 127 | 128 | } 129 | 130 | var pos = line.indexOf( ' ' ); 131 | 132 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; 133 | key = key.toLowerCase(); 134 | 135 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ''; 136 | value = value.trim(); 137 | 138 | if ( key === 'newmtl' ) { 139 | 140 | // New material 141 | 142 | info = { name: value }; 143 | materialsInfo[ value ] = info; 144 | 145 | } else if ( info ) { 146 | 147 | if ( key === 'ka' || key === 'kd' || key === 'ks' ) { 148 | 149 | var ss = value.split( delimiter_pattern, 3 ); 150 | info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ]; 151 | 152 | } else { 153 | 154 | info[ key ] = value; 155 | 156 | } 157 | 158 | } 159 | 160 | } 161 | 162 | var materialCreator = new THREE.MTLLoader.MaterialCreator( this.texturePath || this.path, this.materialOptions ); 163 | materialCreator.setCrossOrigin( this.crossOrigin ); 164 | materialCreator.setManager( this.manager ); 165 | materialCreator.setMaterials( materialsInfo ); 166 | return materialCreator; 167 | 168 | } 169 | 170 | }; 171 | 172 | /** 173 | * Create a new THREE-MTLLoader.MaterialCreator 174 | * @param baseUrl - Url relative to which textures are loaded 175 | * @param options - Set of options on how to construct the materials 176 | * side: Which side to apply the material 177 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide 178 | * wrap: What type of wrapping to apply for textures 179 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping 180 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 181 | * Default: false, assumed to be already normalized 182 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 183 | * Default: false 184 | * @constructor 185 | */ 186 | 187 | THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) { 188 | 189 | this.baseUrl = baseUrl || ''; 190 | this.options = options; 191 | this.materialsInfo = {}; 192 | this.materials = {}; 193 | this.materialsArray = []; 194 | this.nameLookup = {}; 195 | 196 | this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; 197 | this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; 198 | 199 | }; 200 | 201 | THREE.MTLLoader.MaterialCreator.prototype = { 202 | 203 | constructor: THREE.MTLLoader.MaterialCreator, 204 | 205 | crossOrigin: 'Anonymous', 206 | 207 | setCrossOrigin: function ( value ) { 208 | 209 | this.crossOrigin = value; 210 | 211 | }, 212 | 213 | setManager: function ( value ) { 214 | 215 | this.manager = value; 216 | 217 | }, 218 | 219 | setMaterials: function ( materialsInfo ) { 220 | 221 | this.materialsInfo = this.convert( materialsInfo ); 222 | this.materials = {}; 223 | this.materialsArray = []; 224 | this.nameLookup = {}; 225 | 226 | }, 227 | 228 | convert: function ( materialsInfo ) { 229 | 230 | if ( ! this.options ) return materialsInfo; 231 | 232 | var converted = {}; 233 | 234 | for ( var mn in materialsInfo ) { 235 | 236 | // Convert materials info into normalized form based on options 237 | 238 | var mat = materialsInfo[ mn ]; 239 | 240 | var covmat = {}; 241 | 242 | converted[ mn ] = covmat; 243 | 244 | for ( var prop in mat ) { 245 | 246 | var save = true; 247 | var value = mat[ prop ]; 248 | var lprop = prop.toLowerCase(); 249 | 250 | switch ( lprop ) { 251 | 252 | case 'kd': 253 | case 'ka': 254 | case 'ks': 255 | 256 | // Diffuse color (color under white light) using RGB values 257 | 258 | if ( this.options && this.options.normalizeRGB ) { 259 | 260 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 261 | 262 | } 263 | 264 | if ( this.options && this.options.ignoreZeroRGBs ) { 265 | 266 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) { 267 | 268 | // ignore 269 | 270 | save = false; 271 | 272 | } 273 | 274 | } 275 | 276 | break; 277 | 278 | default: 279 | 280 | break; 281 | 282 | } 283 | 284 | if ( save ) { 285 | 286 | covmat[ lprop ] = value; 287 | 288 | } 289 | 290 | } 291 | 292 | } 293 | 294 | return converted; 295 | 296 | }, 297 | 298 | preload: function () { 299 | 300 | for ( var mn in this.materialsInfo ) { 301 | 302 | this.create( mn ); 303 | 304 | } 305 | 306 | }, 307 | 308 | getIndex: function ( materialName ) { 309 | 310 | return this.nameLookup[ materialName ]; 311 | 312 | }, 313 | 314 | getAsArray: function () { 315 | 316 | var index = 0; 317 | 318 | for ( var mn in this.materialsInfo ) { 319 | 320 | this.materialsArray[ index ] = this.create( mn ); 321 | this.nameLookup[ mn ] = index; 322 | index ++; 323 | 324 | } 325 | 326 | return this.materialsArray; 327 | 328 | }, 329 | 330 | create: function ( materialName ) { 331 | 332 | if ( this.materials[ materialName ] === undefined ) { 333 | 334 | this.createMaterial_( materialName ); 335 | 336 | } 337 | 338 | return this.materials[ materialName ]; 339 | 340 | }, 341 | 342 | createMaterial_: function ( materialName ) { 343 | 344 | // Create material 345 | 346 | var scope = this; 347 | var mat = this.materialsInfo[ materialName ]; 348 | var params = { 349 | 350 | name: materialName, 351 | side: this.side 352 | 353 | }; 354 | 355 | function resolveURL( baseUrl, url ) { 356 | 357 | if ( typeof url !== 'string' || url === '' ) 358 | return ''; 359 | 360 | // Absolute URL 361 | if ( /^https?:\/\//i.test( url ) ) return url; 362 | 363 | return baseUrl + url; 364 | 365 | } 366 | 367 | function setMapForType( mapType, value ) { 368 | 369 | if ( params[ mapType ] ) return; // Keep the first encountered texture 370 | 371 | var texParams = scope.getTextureParams( value, params ); 372 | var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) ); 373 | 374 | map.repeat.copy( texParams.scale ); 375 | map.offset.copy( texParams.offset ); 376 | 377 | map.wrapS = scope.wrap; 378 | map.wrapT = scope.wrap; 379 | 380 | params[ mapType ] = map; 381 | 382 | } 383 | 384 | for ( var prop in mat ) { 385 | 386 | var value = mat[ prop ]; 387 | var n; 388 | 389 | if ( value === '' ) continue; 390 | 391 | switch ( prop.toLowerCase() ) { 392 | 393 | // Ns is material specular exponent 394 | 395 | case 'kd': 396 | 397 | // Diffuse color (color under white light) using RGB values 398 | 399 | params.color = new THREE.Color().fromArray( value ); 400 | 401 | break; 402 | 403 | case 'ks': 404 | 405 | // Specular color (color when light is reflected from shiny surface) using RGB values 406 | params.specular = new THREE.Color().fromArray( value ); 407 | 408 | break; 409 | 410 | case 'map_kd': 411 | 412 | // Diffuse texture map 413 | 414 | setMapForType( "map", value ); 415 | 416 | break; 417 | 418 | case 'map_ks': 419 | 420 | // Specular map 421 | 422 | setMapForType( "specularMap", value ); 423 | 424 | break; 425 | 426 | case 'norm': 427 | 428 | setMapForType( "normalMap", value ); 429 | 430 | break; 431 | 432 | case 'map_bump': 433 | case 'bump': 434 | 435 | // Bump texture map 436 | 437 | setMapForType( "bumpMap", value ); 438 | 439 | break; 440 | 441 | case 'ns': 442 | 443 | // The specular exponent (defines the focus of the specular highlight) 444 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 445 | 446 | params.shininess = parseFloat( value ); 447 | 448 | break; 449 | 450 | case 'd': 451 | n = parseFloat( value ); 452 | 453 | if ( n < 1 ) { 454 | 455 | params.opacity = n; 456 | params.transparent = true; 457 | 458 | } 459 | 460 | break; 461 | 462 | case 'tr': 463 | n = parseFloat( value ); 464 | 465 | if ( n > 0 ) { 466 | 467 | params.opacity = 1 - n; 468 | params.transparent = true; 469 | 470 | } 471 | 472 | break; 473 | 474 | default: 475 | break; 476 | 477 | } 478 | 479 | } 480 | 481 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); 482 | return this.materials[ materialName ]; 483 | 484 | }, 485 | 486 | getTextureParams: function ( value, matParams ) { 487 | 488 | var texParams = { 489 | 490 | scale: new THREE.Vector2( 1, 1 ), 491 | offset: new THREE.Vector2( 0, 0 ) 492 | 493 | }; 494 | 495 | var items = value.split( /\s+/ ); 496 | var pos; 497 | 498 | pos = items.indexOf( '-bm' ); 499 | 500 | if ( pos >= 0 ) { 501 | 502 | matParams.bumpScale = parseFloat( items[ pos + 1 ] ); 503 | items.splice( pos, 2 ); 504 | 505 | } 506 | 507 | pos = items.indexOf( '-s' ); 508 | 509 | if ( pos >= 0 ) { 510 | 511 | texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 512 | items.splice( pos, 4 ); // we expect 3 parameters here! 513 | 514 | } 515 | 516 | pos = items.indexOf( '-o' ); 517 | 518 | if ( pos >= 0 ) { 519 | 520 | texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) ); 521 | items.splice( pos, 4 ); // we expect 3 parameters here! 522 | 523 | } 524 | 525 | texParams.url = items.join( ' ' ).trim(); 526 | return texParams; 527 | 528 | }, 529 | 530 | loadTexture: function ( url, mapping, onLoad, onProgress, onError ) { 531 | 532 | var texture; 533 | var loader = THREE.Loader.Handlers.get( url ); 534 | var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager; 535 | 536 | if ( loader === null ) { 537 | 538 | loader = new THREE.TextureLoader( manager ); 539 | 540 | } 541 | 542 | if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin ); 543 | texture = loader.load( url, onLoad, onProgress, onError ); 544 | 545 | if ( mapping !== undefined ) texture.mapping = mapping; 546 | 547 | return texture; 548 | 549 | } 550 | 551 | }; 552 | -------------------------------------------------------------------------------- /examples/index.1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Echarts plugin to support AMap 7 | 8 | 79 | 80 | 81 |
82 |
83 |
84 |
85 |
86 |
上海黄浦区公共网络检测系统
87 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 413 | 414 | 415 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Echarts plugin to support AMap 7 | 8 | 17 | 18 | 19 |
20 | 21 | 22 | 24 | --> 25 | 26 | 27 | 28 | 32 | 33 | 422 | 423 | 424 | -------------------------------------------------------------------------------- /dist/echarts-amap.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/universalModuleDefinition","webpack:///echarts-amap.min.js","webpack:///webpack/bootstrap 6491c7555f12406ee575","webpack:///external \"echarts\"","webpack:///./src/AMapCoordSys.js","webpack:///./src/AMapModel.js","webpack:///./src/AMapView.js","webpack:///./src/amap.js"],"names":["root","factory","exports","module","require","define","amd","this","__WEBPACK_EXTERNAL_MODULE_0__","modules","__webpack_require__","moduleId","installedModules","i","l","call","m","c","value","d","name","getter","o","Object","defineProperty","configurable","enumerable","get","n","__esModule","object","property","prototype","hasOwnProperty","p","s","AMapCoordSys","amap","api","_amap","dimensions","_mapOffset","_api","echarts","setZoom","zoom","_zoom","setCenter","center","_center","lnglatToPixel","setMapOffset","mapOffset","getAMap","dataToPoint","data","point","AMap","LngLat","px","lngLatToContainer","x","y","pointToData","pt","containerToLngLat","lng","lat","getViewRect","graphic","BoundingRect","getWidth","getHeight","getRoamTransform","matrix","create","ecModel","amapCoordSys","getDom","eachComponent","amapModel","viewportRoot","getZr","painter","getViewportRoot","Error","__amap","amapRoot","querySelector","style","left","top","removeChild","document","createElement","cssText","classList","add","appendChild","options","__options","util","clone","Map","layer","__layer","CustomLayer","setMap","getLayer","hide","getZoom","getCenter","__mapOffset","coordinateSystem","show","eachSeries","seriesModel","v2Equal","a","b","extendComponentModel","type","getMapOptions","setCenterAndZoom","option","centerOrZoomChanged","defaultOption","throttle","fn","time","context","lock","args","wrapperFn","later","apply","arguments","setTimeout","extendComponentView","render","aMapModel","zoomEndHandler","rendering","dispatchAction","resizeHandler","e","getInstanceByDom","resize","moveHandler","coordSys","offsetEl","parentNode","parseInt","throttledResizeHandler","off","_oldMoveHandler","_oldZoomEndHandler","_oldResizeHandler","on","registerCoordinateSystem","registerAction","event","update","payload","version"],"mappings":"CAAA,SAAAA,EAAAC,GACA,gBAAAC,UAAA,gBAAAC,QACAA,OAAAD,QAAAD,EAAAG,QAAA,YACA,kBAAAC,gBAAAC,IACAD,QAAA,WAAAJ,GACA,gBAAAC,SACAA,QAAA,KAAAD,EAAAG,QAAA,aAEAJ,EAAA,QAAAA,EAAA,YAAyCA,EAAA,aAAAC,EAAAD,EAAA,WACxCO,KAAA,SAAAC,GACD,MCAgB,UAAUC,GCN1B,QAAAC,GAAAC,GAGA,GAAAC,EAAAD,GACA,MAAAC,GAAAD,GAAAT,OAGA,IAAAC,GAAAS,EAAAD,IACAE,EAAAF,EACAG,GAAA,EACAZ,WAUA,OANAO,GAAAE,GAAAI,KAAAZ,EAAAD,QAAAC,IAAAD,QAAAQ,GAGAP,EAAAW,GAAA,EAGAX,EAAAD,QAvBA,GAAAU,KA+DA,OAnCAF,GAAAM,EAAAP,EAGAC,EAAAO,EAAAL,EAGAF,EAAAG,EAAA,SAAAK,GAA2C,MAAAA,IAG3CR,EAAAS,EAAA,SAAAjB,EAAAkB,EAAAC,GACAX,EAAAY,EAAApB,EAAAkB,IACAG,OAAAC,eAAAtB,EAAAkB,GACAK,cAAA,EACAC,YAAA,EACAC,IAAAN,KAMAX,EAAAkB,EAAA,SAAAzB,GACA,GAAAkB,GAAAlB,KAAA0B,WACA,WAA2B,MAAA1B,GAAA,SAC3B,WAAiC,MAAAA,GAEjC,OADAO,GAAAS,EAAAE,EAAA,IAAAA,GACAA,GAIAX,EAAAY,EAAA,SAAAQ,EAAAC,GAAsD,MAAAR,QAAAS,UAAAC,eAAAlB,KAAAe,EAAAC,IAGtDrB,EAAAwB,EAAA,GAGAxB,IAAAyB,EAAA,KDgBM,SAAUhC,EAAQD,GEhFxBC,EAAAD,QAAAM,GFsFM,SAAUL,EAAQD,EAASQ,GGpFjC,QAAA0B,GAAAC,EAAAC,GACA/B,KAAAgC,MAAAF,EACA9B,KAAAiC,YAAA,aACAjC,KAAAkC,YAAA,KAEAlC,KAAAmC,KAAAJ,EAPA,GAAAK,GAAAjC,EAAA,EAUA0B,GAAAJ,UAAAQ,YAAA,aAEAJ,EAAAJ,UAAAY,QAAA,SAAAC,GACAtC,KAAAuC,MAAAD,GAGAT,EAAAJ,UAAAe,UAAA,SAAAC,GACAzC,KAAA0C,QAAA1C,KAAAgC,MAAAW,cAAAF,IAGAZ,EAAAJ,UAAAmB,aAAA,SAAAC,GACA7C,KAAAkC,WAAAW,GAGAhB,EAAAJ,UAAAqB,QAAA,WACA,MAAA9C,MAAAgC,OAGAH,EAAAJ,UAAAsB,YAAA,SAAAC,GACA,GAAAC,GAAA,GAAAC,MAAAC,OAAAH,EAAA,GAAAA,EAAA,IACAI,EAAApD,KAAAgC,MAAAqB,kBAAAJ,GACAJ,EAAA7C,KAAAkC,UACA,QAAAkB,EAAAE,EAAAT,EAAA,GAAAO,EAAAG,EAAAV,EAAA,KAGAhB,EAAAJ,UAAA+B,YAAA,SAAAC,GACA,GAAAZ,GAAA7C,KAAAkC,WACAuB,EAAAzD,KAAAgC,MAAA0B,mBACAJ,EAAAG,EAAA,GAAAZ,EAAA,GACAU,EAAAE,EAAA,GAAAZ,EAAA,IAEA,QAAAY,EAAAE,IAAAF,EAAAG,MAGA/B,EAAAJ,UAAAoC,YAAA,WACA,GAAA9B,GAAA/B,KAAAmC,IACA,WAAAC,GAAA0B,QAAAC,aAAA,IAAAhC,EAAAiC,WAAAjC,EAAAkC,cAGApC,EAAAJ,UAAAyC,iBAAA,WACA,MAAA9B,GAAA+B,OAAAC,SAMAvC,GAAAI,WAAAJ,EAAAJ,UAAAQ,WAEAJ,EAAAuC,OAAA,SAAAC,EAAAtC,GACA,GAAAuC,GACA7E,EAAAsC,EAAAwC,QAGAF,GAAAG,cAAA,gBAAAC,GACA,GAAAC,GAAA3C,EAAA4C,QAAAC,QAAAC,iBACA,uBAAA3B,MACA,SAAA4B,OAAA,yBAGA,IAAAR,EACA,SAAAQ,OAAA,oCAEA,KAAAL,EAAAM,OAAA,CAEA,GAAAC,GAAAvF,EAAAwF,cAAA,qBACAD,KAGAN,EAAAQ,MAAAC,KAAA,MACAT,EAAAQ,MAAAE,IAAA,MACA3F,EAAA4F,YAAAL,IAEAA,EAAAM,SAAAC,cAAA,OACAP,EAAAE,MAAAM,QAAA,yBAEAR,EAAAS,UAAAC,IAAA,qBACAjG,EAAAkG,YAAAX,EAEA,IAAAY,GAAAnB,EAAArD,SACAwE,GAAAnB,EAAAoB,UAAAzD,EAAA0D,KAAAC,MAAAH,EACA,IAAA9D,GAAA2C,EAAAM,OAAA,GAAA7B,MAAA8C,IAAAhB,EAAAY,GAEAK,EAAAxB,EAAAyB,QAAA,GAAAhD,MAAAiD,YAAAzB,EACAuB,GAAAG,OAAAtE,GAEA,GAAAA,GAAA2C,EAAA3B,UACAmD,EAAAxB,EAAA4B,UACAJ,GAAAK,MAEA,IAAAhE,GAAAR,EAAAyE,UACA9D,EAAAX,EAAA0E,WAEAlC,GAAA,GAAAzC,GAAAC,EAAAC,GACAuC,EAAA1B,aAAA6B,EAAAgC,cAAA,MACAnC,EAAAjC,QAAAC,GACAgC,EAAA9B,WAAAC,EAAAkB,IAAAlB,EAAAmB,MAEAa,EAAAiC,iBAAApC,EACA2B,EAAAU,SAGAtC,EAAAuC,WAAA,SAAAC,GACA,SAAAA,EAAAzF,IAAA,sBACAyF,EAAAH,iBAAApC,MAKA1E,EAAAD,QAAAkC,GH6FM,SAAUjC,EAAQD,EAASQ,GInNjC,QAAA2G,GAAAC,EAAAC,GACA,MAAAD,IAAAC,GAAAD,EAAA,KAAAC,EAAA,IAAAD,EAAA,KAAAC,EAAA,GAGApH,EAAAD,QAAAQ,EAAA,GAAA8G,sBACAC,KAAA,OAEApE,QAAA,WAEA,MAAA9C,MAAA+E,QAGAsB,SAAA,WAEA,MAAArG,MAAAkG,SAGAiB,cAAA,WACA,MAAAnH,MAAA6F,WAGAuB,iBAAA,SAAA3E,EAAAH,GACAtC,KAAAqH,OAAA5E,SACAzC,KAAAqH,OAAA/E,QAEAgF,oBAAA,SAAA7E,EAAAH,GACA,GAAA+E,GAAArH,KAAAqH,MACA,SAAAP,EAAArE,EAAA4E,EAAA5E,SAAAH,IAAA+E,EAAA/E,OAGAiF,eACA9E,QAAA,sBACAH,KAAA,MJ4NM,SAAU1C,EAAQD,EAASQ,GKxPjC,QAAAqH,GAAAC,EAAAC,EAAAC,GACA,GAAAC,GAAAC,EAAAC,EAAAC,CAwBA,OAtBAA,GAAA,WAEAH,GAAA,EACAC,IACAC,EAAAE,MAAAL,EAAAE,GACAA,GAAA,IAIAC,EAAA,WACAF,EAEAC,EAAAI,WAIAR,EAAAO,MAAAL,EAAAM,WACAC,WAAAH,EAAAL,GACAE,GAAA,IAOA,GAAAxF,GAAAjC,EAAA,EAEAP,GAAAD,QAAAQ,EAAA,GAAAgI,qBACAjB,KAAA,OACAkB,OAAA,SAAAC,EAAAhE,EAAAtC,GA0BA,QAAAuG,KACAC,GAGAxG,EAAAyG,gBACAtB,KAAA,aAIA,QAAAuB,GAAAC,GACAtG,EAAAuG,iBAAA5G,EAAAwC,UAAAqE,SACAC,EAAArI,KAAAR,KAAA0I,GApCA,GAAAH,IAAA,EAEAzG,EAAAuG,EAAAvF,UACA4B,EAAA3C,EAAA4C,QAAAC,QAAAC,kBACAiE,EAAAT,EAAA3B,iBACAmC,EAAA,SAAAH,GACA,IAAAH,EAAA,CAGA,GAAAQ,GAAArE,EAAAsE,iCACAnG,IACAoG,SAAAF,EAAA7D,MAAAC,KAAA,QACA8D,SAAAF,EAAA7D,MAAAE,IAAA,OAEAV,GAAAQ,MAAAC,KAAAtC,EAAA,QACA6B,EAAAQ,MAAAE,IAAAvC,EAAA,QAEAiG,EAAAlG,aAAAC,GACAwF,EAAA5B,YAAA5D,EAEAd,EAAAyG,gBACAtB,KAAA,eAkBAgC,EAAA1B,EAAAiB,EAAA,IAAA3G,EAEAA,GAAAqH,IAAA,YAAAnJ,KAAAoJ,iBACAtH,EAAAqH,IAAA,UAAAnJ,KAAAqJ,oBACAvH,EAAAqH,IAAA,UAAAnJ,KAAAqJ,oBACAvH,EAAAqH,IAAA,WAAAnJ,KAAAqJ,oBACAhB,EAAAjH,IAAA,iBAAAU,EAAAqH,IAAA,SAAAnJ,KAAAsJ,mBAEAxH,EAAAyH,GAAA,YAAAV,GACA/G,EAAAyH,GAAA,UAAAjB,GACAxG,EAAAyH,GAAA,UAAAjB,GACAxG,EAAAyH,GAAA,WAAAjB,GACAD,EAAAjH,IAAA,iBAAAU,EAAAyH,GAAA,SAAAL,GAEAlJ,KAAAoJ,gBAAAP,EACA7I,KAAAqJ,mBAAAf,EACAtI,KAAAsJ,kBAAAJ,EAoBAX,GAAA,MLqQM,SAAU3I,EAAQD,EAASQ,GMlXjCA,EAAA,GAAAqJ,yBACA,OAAArJ,EAAA,IAEAA,EAAA,GACAA,EAAA,GAGAA,EAAA,GAAAsJ,gBACAvC,KAAA,WACAwC,MAAA,WACAC,OAAA,gBACC,SAAAC,EAAAvF,GACDA,EAAAG,cAAA,gBAAA6D,GACA,GAAAvG,GAAAuG,EAAAvF,UACAL,EAAAX,EAAA0E,WACA6B,GAAAjB,kBAAA3E,EAAAkB,IAAAlB,EAAAmB,KAAA9B,EAAAyE,eAIA3G,EAAAD,SACAkK,QAAA","file":"echarts-amap.min.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"echarts\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"echarts\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"amap\"] = factory(require(\"echarts\"));\n\telse\n\t\troot[\"echarts\"] = root[\"echarts\"] || {}, root[\"echarts\"][\"amap\"] = factory(root[\"echarts\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {\nreturn \n\n\n// WEBPACK FOOTER //\n// webpack/universalModuleDefinition","(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory(require(\"echarts\"));\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([\"echarts\"], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"amap\"] = factory(require(\"echarts\"));\n\telse\n\t\troot[\"echarts\"] = root[\"echarts\"] || {}, root[\"echarts\"][\"amap\"] = factory(root[\"echarts\"]);\n})(this, function(__WEBPACK_EXTERNAL_MODULE_0__) {\nreturn /******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// identity function for calling harmony imports with the correct context\n/******/ \t__webpack_require__.i = function(value) { return value; };\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, {\n/******/ \t\t\t\tconfigurable: false,\n/******/ \t\t\t\tenumerable: true,\n/******/ \t\t\t\tget: getter\n/******/ \t\t\t});\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = 4);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports) {\n\nmodule.exports = __WEBPACK_EXTERNAL_MODULE_0__;\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar echarts = __webpack_require__(0);\r\n\r\nfunction AMapCoordSys(amap, api) {\r\n this._amap = amap;\r\n this.dimensions = ['lng', 'lat'];\r\n this._mapOffset = [0, 0];\r\n\r\n this._api = api;\r\n}\r\n\r\nAMapCoordSys.prototype.dimensions = ['lng', 'lat'];\r\n\r\nAMapCoordSys.prototype.setZoom = function (zoom) {\r\n this._zoom = zoom;\r\n};\r\n\r\nAMapCoordSys.prototype.setCenter = function (center) {\r\n this._center = this._amap.lnglatToPixel(center);//, 10)\r\n};\r\n\r\nAMapCoordSys.prototype.setMapOffset = function (mapOffset) {\r\n this._mapOffset = mapOffset;\r\n};\r\n\r\nAMapCoordSys.prototype.getAMap = function () {\r\n return this._amap;\r\n};\r\n\r\nAMapCoordSys.prototype.dataToPoint = function (data) {\r\n var point = new AMap.LngLat(data[0], data[1]);\r\n var px = this._amap.lngLatToContainer(point);//, this._zoom);\r\n var mapOffset = this._mapOffset;\r\n return [px.x - mapOffset[0], px.y - mapOffset[1]];\r\n};\r\n\r\nAMapCoordSys.prototype.pointToData = function (pt) {\r\n var mapOffset = this._mapOffset;\r\n var pt = this._amap.containerToLngLat({\r\n x: pt[0] + mapOffset[0],\r\n y: pt[1] + mapOffset[1]\r\n });\r\n return [pt.lng, pt.lat];\r\n};\r\n\r\nAMapCoordSys.prototype.getViewRect = function () {\r\n var api = this._api;\r\n return new echarts.graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight());\r\n};\r\n\r\nAMapCoordSys.prototype.getRoamTransform = function () {\r\n return echarts.matrix.create();\r\n};\r\n\r\nvar Overlay;\r\n\r\n// For deciding which dimensions to use when creating list data\r\nAMapCoordSys.dimensions = AMapCoordSys.prototype.dimensions;\r\n\r\nAMapCoordSys.create = function (ecModel, api) {\r\n var amapCoordSys;\r\n var root = api.getDom();\r\n\r\n // TODO Dispose\r\n ecModel.eachComponent('amap', function (amapModel) {\r\n var viewportRoot = api.getZr().painter.getViewportRoot();\r\n if (typeof AMap === 'undefined') {\r\n throw new Error('AMap api is not loaded');\r\n }\r\n\r\n if (amapCoordSys) {\r\n throw new Error('Only one amap component can exist');\r\n }\r\n if (!amapModel.__amap) {\r\n // Not support IE8\r\n var amapRoot = root.querySelector('.ec-extension-amap');\r\n if (amapRoot) {\r\n // Reset viewport left and top, which will be changed\r\n // in moving handler in AMapView\r\n viewportRoot.style.left = '0px';\r\n viewportRoot.style.top = '0px';\r\n root.removeChild(amapRoot);\r\n }\r\n amapRoot = document.createElement('div');\r\n amapRoot.style.cssText = 'width:100%;height:100%';\r\n // Not support IE8\r\n amapRoot.classList.add('ec-extension-amap');\r\n root.appendChild(amapRoot);\r\n\r\n var options = amapModel.get() || {};\r\n options = amapModel.__options = echarts.util.clone(options);\r\n var amap = amapModel.__amap = new AMap.Map(amapRoot, options);\r\n\r\n var layer = amapModel.__layer = new AMap.CustomLayer(viewportRoot);\r\n layer.setMap(amap);\r\n }\r\n var amap = amapModel.getAMap();\r\n var layer = amapModel.getLayer();\r\n layer.hide();\r\n\r\n var zoom = amap.getZoom();\r\n var center = amap.getCenter();\r\n\r\n amapCoordSys = new AMapCoordSys(amap, api);\r\n amapCoordSys.setMapOffset(amapModel.__mapOffset || [0, 0]);\r\n amapCoordSys.setZoom(zoom);\r\n amapCoordSys.setCenter([center.lng, center.lat]);\r\n\r\n amapModel.coordinateSystem = amapCoordSys;\r\n layer.show();\r\n });\r\n\r\n ecModel.eachSeries(function (seriesModel) {\r\n if (seriesModel.get('coordinateSystem') === 'amap') {\r\n seriesModel.coordinateSystem = amapCoordSys;\r\n }\r\n });\r\n};\r\n\r\nmodule.exports = AMapCoordSys;\r\n\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports, __webpack_require__) {\n\nfunction v2Equal(a, b) {\r\n return a && b && a[0] === b[0] && a[1] === b[1];\r\n}\r\n\r\nmodule.exports = __webpack_require__(0).extendComponentModel({\r\n type: 'amap',\r\n\r\n getAMap: function () {\r\n // __amap is injected when creating AMapCoordSys\r\n return this.__amap;\r\n },\r\n\r\n getLayer: function() {\r\n // __layer is injected when creating AMapCoordSys\r\n return this.__layer;\r\n },\r\n\r\n getMapOptions: function() {\r\n return this.__options;\r\n },\r\n\r\n setCenterAndZoom: function (center, zoom) {\r\n this.option.center = center;\r\n this.option.zoom = zoom;\r\n },\r\n centerOrZoomChanged: function (center, zoom) {\r\n var option = this.option;\r\n return !(v2Equal(center, option.center) && zoom === option.zoom);\r\n },\r\n\r\n defaultOption: {\r\n center: [116.397475,39.908695],\r\n zoom: 4,\r\n }\r\n});\r\n\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/*\r\n * this function bollowed from:\r\n * https://github.com/Leaflet/Leaflet/blob/master/src/core/Util.js\r\n */\r\nfunction throttle(fn, time, context) {\r\n\tvar lock, args, wrapperFn, later;\r\n\r\n\tlater = function () {\r\n\t\t// reset lock and call if queued\r\n\t\tlock = false;\r\n\t\tif (args) {\r\n\t\t\twrapperFn.apply(context, args);\r\n\t\t\targs = false;\r\n\t\t}\r\n\t};\r\n\r\n\twrapperFn = function () {\r\n\t\tif (lock) {\r\n\t\t\t// called too soon, queue to call later\r\n\t\t\targs = arguments;\r\n\r\n\t\t} else {\r\n\t\t\t// call and lock until later\r\n\t\t\tfn.apply(context, arguments);\r\n\t\t\tsetTimeout(later, time);\r\n\t\t\tlock = true;\r\n\t\t}\r\n\t};\r\n\r\n\treturn wrapperFn;\r\n}\r\n\r\nvar echarts = __webpack_require__(0);\r\n\r\nmodule.exports = __webpack_require__(0).extendComponentView({\r\n type: 'amap',\r\n render: function (aMapModel, ecModel, api) {\r\n var rendering = true;\r\n\r\n var amap = aMapModel.getAMap();\r\n var viewportRoot = api.getZr().painter.getViewportRoot();\r\n var coordSys = aMapModel.coordinateSystem;\r\n var moveHandler = function (e) {\r\n if (rendering) {\r\n return;\r\n }\r\n var offsetEl = viewportRoot.parentNode.parentNode.parentNode;\r\n var mapOffset = [\r\n -parseInt(offsetEl.style.left, 10) || 0,\r\n -parseInt(offsetEl.style.top, 10) || 0\r\n ];\r\n viewportRoot.style.left = mapOffset[0] + 'px';\r\n viewportRoot.style.top = mapOffset[1] + 'px';\r\n\r\n coordSys.setMapOffset(mapOffset);\r\n aMapModel.__mapOffset = mapOffset;\r\n\r\n api.dispatchAction({\r\n type: 'amapRoam'\r\n });\r\n };\r\n\r\n function zoomEndHandler() {\r\n if (rendering) {\r\n return;\r\n }\r\n api.dispatchAction({\r\n type: 'amapRoam'\r\n });\r\n }\r\n\r\n function resizeHandler(e) {\r\n echarts.getInstanceByDom(api.getDom()).resize();\r\n moveHandler.call(this, e)\r\n }\r\n\r\n var throttledResizeHandler = throttle(resizeHandler, 300, amap);\r\n\r\n amap.off('movestart', this._oldMoveHandler);\r\n amap.off('zoomend', this._oldZoomEndHandler);\r\n amap.off('moveend', this._oldZoomEndHandler);\r\n amap.off('complete', this._oldZoomEndHandler);\r\n aMapModel.get('resizeEnable') && amap.off('resize', this._oldResizeHandler);\r\n\r\n amap.on('movestart', moveHandler);\r\n amap.on('zoomend', zoomEndHandler);\r\n amap.on('moveend', zoomEndHandler);\r\n amap.on('complete', zoomEndHandler);\r\n aMapModel.get('resizeEnable') && amap.on('resize', throttledResizeHandler);\r\n\r\n this._oldMoveHandler = moveHandler;\r\n this._oldZoomEndHandler = zoomEndHandler;\r\n this._oldResizeHandler = throttledResizeHandler;\r\n\r\n // var roam = aMapModel.get('roam');\r\n // if (roam && roam !== 'scale') {\r\n // amap.enableDragging();\r\n // }\r\n // else {\r\n // amap.disableDragging();\r\n // }\r\n // if (roam && roam !== 'move') {\r\n // amap.enableScrollWheelZoom();\r\n // amap.enableDoubleClickZoom();\r\n // amap.enablePinchToZoom();\r\n // }\r\n // else {\r\n // amap.disableScrollWheelZoom();\r\n // amap.disableDoubleClickZoom();\r\n // amap.disablePinchToZoom();\r\n // }\r\n\r\n rendering = false;\r\n }\r\n});\r\n\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports, __webpack_require__) {\n\n/**\r\n * BMap component extension\r\n */\r\n__webpack_require__(0).registerCoordinateSystem(\r\n 'amap', __webpack_require__(1)\r\n);\r\n__webpack_require__(2);\r\n__webpack_require__(3);\r\n\r\n// Action\r\n__webpack_require__(0).registerAction({\r\n type: 'amapRoam',\r\n event: 'amapRoam',\r\n update: 'updateLayout'\r\n}, function (payload, ecModel) {\r\n ecModel.eachComponent('amap', function (aMapModel) {\r\n var amap = aMapModel.getAMap();\r\n var center = amap.getCenter();\r\n aMapModel.setCenterAndZoom([center.lng, center.lat], amap.getZoom());\r\n });\r\n});\r\n\r\nmodule.exports = {\r\n version: \"1.0.0-rc.6\"\r\n};\r\n\n\n/***/ })\n/******/ ]);\n});\n\n\n// WEBPACK FOOTER //\n// echarts-amap.min.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// identity function for calling harmony imports with the correct context\n \t__webpack_require__.i = function(value) { return value; };\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, {\n \t\t\t\tconfigurable: false,\n \t\t\t\tenumerable: true,\n \t\t\t\tget: getter\n \t\t\t});\n \t\t}\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 4);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 6491c7555f12406ee575","module.exports = __WEBPACK_EXTERNAL_MODULE_0__;\n\n\n//////////////////\n// WEBPACK FOOTER\n// external \"echarts\"\n// module id = 0\n// module chunks = 0","var echarts = require('echarts');\r\n\r\nfunction AMapCoordSys(amap, api) {\r\n this._amap = amap;\r\n this.dimensions = ['lng', 'lat'];\r\n this._mapOffset = [0, 0];\r\n\r\n this._api = api;\r\n}\r\n\r\nAMapCoordSys.prototype.dimensions = ['lng', 'lat'];\r\n\r\nAMapCoordSys.prototype.setZoom = function (zoom) {\r\n this._zoom = zoom;\r\n};\r\n\r\nAMapCoordSys.prototype.setCenter = function (center) {\r\n this._center = this._amap.lnglatToPixel(center);//, 10)\r\n};\r\n\r\nAMapCoordSys.prototype.setMapOffset = function (mapOffset) {\r\n this._mapOffset = mapOffset;\r\n};\r\n\r\nAMapCoordSys.prototype.getAMap = function () {\r\n return this._amap;\r\n};\r\n\r\nAMapCoordSys.prototype.dataToPoint = function (data) {\r\n var point = new AMap.LngLat(data[0], data[1]);\r\n var px = this._amap.lngLatToContainer(point);//, this._zoom);\r\n var mapOffset = this._mapOffset;\r\n return [px.x - mapOffset[0], px.y - mapOffset[1]];\r\n};\r\n\r\nAMapCoordSys.prototype.pointToData = function (pt) {\r\n var mapOffset = this._mapOffset;\r\n var pt = this._amap.containerToLngLat({\r\n x: pt[0] + mapOffset[0],\r\n y: pt[1] + mapOffset[1]\r\n });\r\n return [pt.lng, pt.lat];\r\n};\r\n\r\nAMapCoordSys.prototype.getViewRect = function () {\r\n var api = this._api;\r\n return new echarts.graphic.BoundingRect(0, 0, api.getWidth(), api.getHeight());\r\n};\r\n\r\nAMapCoordSys.prototype.getRoamTransform = function () {\r\n return echarts.matrix.create();\r\n};\r\n\r\nvar Overlay;\r\n\r\n// For deciding which dimensions to use when creating list data\r\nAMapCoordSys.dimensions = AMapCoordSys.prototype.dimensions;\r\n\r\nAMapCoordSys.create = function (ecModel, api) {\r\n var amapCoordSys;\r\n var root = api.getDom();\r\n\r\n // TODO Dispose\r\n ecModel.eachComponent('amap', function (amapModel) {\r\n var viewportRoot = api.getZr().painter.getViewportRoot();\r\n if (typeof AMap === 'undefined') {\r\n throw new Error('AMap api is not loaded');\r\n }\r\n\r\n if (amapCoordSys) {\r\n throw new Error('Only one amap component can exist');\r\n }\r\n if (!amapModel.__amap) {\r\n // Not support IE8\r\n var amapRoot = root.querySelector('.ec-extension-amap');\r\n if (amapRoot) {\r\n // Reset viewport left and top, which will be changed\r\n // in moving handler in AMapView\r\n viewportRoot.style.left = '0px';\r\n viewportRoot.style.top = '0px';\r\n root.removeChild(amapRoot);\r\n }\r\n amapRoot = document.createElement('div');\r\n amapRoot.style.cssText = 'width:100%;height:100%';\r\n // Not support IE8\r\n amapRoot.classList.add('ec-extension-amap');\r\n root.appendChild(amapRoot);\r\n\r\n var options = amapModel.get() || {};\r\n options = amapModel.__options = echarts.util.clone(options);\r\n var amap = amapModel.__amap = new AMap.Map(amapRoot, options);\r\n\r\n var layer = amapModel.__layer = new AMap.CustomLayer(viewportRoot);\r\n layer.setMap(amap);\r\n }\r\n var amap = amapModel.getAMap();\r\n var layer = amapModel.getLayer();\r\n layer.hide();\r\n\r\n var zoom = amap.getZoom();\r\n var center = amap.getCenter();\r\n\r\n amapCoordSys = new AMapCoordSys(amap, api);\r\n amapCoordSys.setMapOffset(amapModel.__mapOffset || [0, 0]);\r\n amapCoordSys.setZoom(zoom);\r\n amapCoordSys.setCenter([center.lng, center.lat]);\r\n\r\n amapModel.coordinateSystem = amapCoordSys;\r\n layer.show();\r\n });\r\n\r\n ecModel.eachSeries(function (seriesModel) {\r\n if (seriesModel.get('coordinateSystem') === 'amap') {\r\n seriesModel.coordinateSystem = amapCoordSys;\r\n }\r\n });\r\n};\r\n\r\nmodule.exports = AMapCoordSys;\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/AMapCoordSys.js\n// module id = 1\n// module chunks = 0","function v2Equal(a, b) {\r\n return a && b && a[0] === b[0] && a[1] === b[1];\r\n}\r\n\r\nmodule.exports = require('echarts').extendComponentModel({\r\n type: 'amap',\r\n\r\n getAMap: function () {\r\n // __amap is injected when creating AMapCoordSys\r\n return this.__amap;\r\n },\r\n\r\n getLayer: function() {\r\n // __layer is injected when creating AMapCoordSys\r\n return this.__layer;\r\n },\r\n\r\n getMapOptions: function() {\r\n return this.__options;\r\n },\r\n\r\n setCenterAndZoom: function (center, zoom) {\r\n this.option.center = center;\r\n this.option.zoom = zoom;\r\n },\r\n centerOrZoomChanged: function (center, zoom) {\r\n var option = this.option;\r\n return !(v2Equal(center, option.center) && zoom === option.zoom);\r\n },\r\n\r\n defaultOption: {\r\n center: [116.397475,39.908695],\r\n zoom: 4,\r\n }\r\n});\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/AMapModel.js\n// module id = 2\n// module chunks = 0","/*\r\n * this function bollowed from:\r\n * https://github.com/Leaflet/Leaflet/blob/master/src/core/Util.js\r\n */\r\nfunction throttle(fn, time, context) {\r\n\tvar lock, args, wrapperFn, later;\r\n\r\n\tlater = function () {\r\n\t\t// reset lock and call if queued\r\n\t\tlock = false;\r\n\t\tif (args) {\r\n\t\t\twrapperFn.apply(context, args);\r\n\t\t\targs = false;\r\n\t\t}\r\n\t};\r\n\r\n\twrapperFn = function () {\r\n\t\tif (lock) {\r\n\t\t\t// called too soon, queue to call later\r\n\t\t\targs = arguments;\r\n\r\n\t\t} else {\r\n\t\t\t// call and lock until later\r\n\t\t\tfn.apply(context, arguments);\r\n\t\t\tsetTimeout(later, time);\r\n\t\t\tlock = true;\r\n\t\t}\r\n\t};\r\n\r\n\treturn wrapperFn;\r\n}\r\n\r\nvar echarts = require('echarts');\r\n\r\nmodule.exports = require('echarts').extendComponentView({\r\n type: 'amap',\r\n render: function (aMapModel, ecModel, api) {\r\n var rendering = true;\r\n\r\n var amap = aMapModel.getAMap();\r\n var viewportRoot = api.getZr().painter.getViewportRoot();\r\n var coordSys = aMapModel.coordinateSystem;\r\n var moveHandler = function (e) {\r\n if (rendering) {\r\n return;\r\n }\r\n var offsetEl = viewportRoot.parentNode.parentNode.parentNode;\r\n var mapOffset = [\r\n -parseInt(offsetEl.style.left, 10) || 0,\r\n -parseInt(offsetEl.style.top, 10) || 0\r\n ];\r\n viewportRoot.style.left = mapOffset[0] + 'px';\r\n viewportRoot.style.top = mapOffset[1] + 'px';\r\n\r\n coordSys.setMapOffset(mapOffset);\r\n aMapModel.__mapOffset = mapOffset;\r\n\r\n api.dispatchAction({\r\n type: 'amapRoam'\r\n });\r\n };\r\n\r\n function zoomEndHandler() {\r\n if (rendering) {\r\n return;\r\n }\r\n api.dispatchAction({\r\n type: 'amapRoam'\r\n });\r\n }\r\n\r\n function resizeHandler(e) {\r\n echarts.getInstanceByDom(api.getDom()).resize();\r\n moveHandler.call(this, e)\r\n }\r\n\r\n var throttledResizeHandler = throttle(resizeHandler, 300, amap);\r\n\r\n amap.off('movestart', this._oldMoveHandler);\r\n amap.off('zoomend', this._oldZoomEndHandler);\r\n amap.off('moveend', this._oldZoomEndHandler);\r\n amap.off('complete', this._oldZoomEndHandler);\r\n aMapModel.get('resizeEnable') && amap.off('resize', this._oldResizeHandler);\r\n\r\n amap.on('movestart', moveHandler);\r\n amap.on('zoomend', zoomEndHandler);\r\n amap.on('moveend', zoomEndHandler);\r\n amap.on('complete', zoomEndHandler);\r\n aMapModel.get('resizeEnable') && amap.on('resize', throttledResizeHandler);\r\n\r\n this._oldMoveHandler = moveHandler;\r\n this._oldZoomEndHandler = zoomEndHandler;\r\n this._oldResizeHandler = throttledResizeHandler;\r\n\r\n // var roam = aMapModel.get('roam');\r\n // if (roam && roam !== 'scale') {\r\n // amap.enableDragging();\r\n // }\r\n // else {\r\n // amap.disableDragging();\r\n // }\r\n // if (roam && roam !== 'move') {\r\n // amap.enableScrollWheelZoom();\r\n // amap.enableDoubleClickZoom();\r\n // amap.enablePinchToZoom();\r\n // }\r\n // else {\r\n // amap.disableScrollWheelZoom();\r\n // amap.disableDoubleClickZoom();\r\n // amap.disablePinchToZoom();\r\n // }\r\n\r\n rendering = false;\r\n }\r\n});\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/AMapView.js\n// module id = 3\n// module chunks = 0","/**\r\n * BMap component extension\r\n */\r\nrequire('echarts').registerCoordinateSystem(\r\n 'amap', require('./AMapCoordSys')\r\n);\r\nrequire('./AMapModel');\r\nrequire('./AMapView');\r\n\r\n// Action\r\nrequire('echarts').registerAction({\r\n type: 'amapRoam',\r\n event: 'amapRoam',\r\n update: 'updateLayout'\r\n}, function (payload, ecModel) {\r\n ecModel.eachComponent('amap', function (aMapModel) {\r\n var amap = aMapModel.getAMap();\r\n var center = amap.getCenter();\r\n aMapModel.setCenterAndZoom([center.lng, center.lat], amap.getZoom());\r\n });\r\n});\r\n\r\nmodule.exports = {\r\n version: process.env.VERSION\r\n};\r\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./src/amap.js\n// module id = 4\n// module chunks = 0"],"sourceRoot":""} -------------------------------------------------------------------------------- /assets/loader/OBJLoader2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Kai Salmen / https://kaisalmen.de 3 | * Development repository: https://github.com/kaisalmen/WWOBJLoader 4 | */ 5 | 6 | 'use strict'; 7 | 8 | if (THREE.OBJLoader2 === undefined) { 9 | THREE.OBJLoader2 = {} 10 | } 11 | 12 | if (THREE.LoaderSupport === undefined) console.error('"THREE.LoaderSupport" is not available. "THREE.OBJLoader2" requires it. Please include "LoaderSupport.js" in your HTML.'); 13 | 14 | /** 15 | * Use this class to load OBJ data from files or to parse OBJ data from an arraybuffer 16 | * @class 17 | * 18 | * @param {THREE.DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link THREE.DefaultLoadingManager} 19 | * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used 20 | */ 21 | THREE.OBJLoader2 = (function() { 22 | 23 | var OBJLOADER2_VERSION = '2.2.1'; 24 | var LoaderBase = THREE.LoaderSupport.LoaderBase; 25 | var Validator = THREE.LoaderSupport.Validator; 26 | var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; 27 | 28 | OBJLoader2.prototype = Object.create(THREE.LoaderSupport.LoaderBase.prototype); 29 | OBJLoader2.prototype.constructor = OBJLoader2; 30 | 31 | function OBJLoader2(manager, logger) { 32 | THREE.LoaderSupport.LoaderBase.call(this, manager, logger); 33 | this.logger.logInfo('Using THREE.OBJLoader2 version: ' + OBJLOADER2_VERSION); 34 | 35 | this.materialPerSmoothingGroup = false; 36 | this.fileLoader = Validator.verifyInput(this.fileLoader, new THREE.FileLoader(this.manager)); 37 | 38 | this.workerSupport = null; 39 | this.terminateWorkerOnLoad = true; 40 | }; 41 | 42 | /** 43 | * Tells whether a material shall be created per smoothing group. 44 | * @memberOf THREE.OBJLoader2 45 | * 46 | * @param {boolean} materialPerSmoothingGroup=false 47 | */ 48 | OBJLoader2.prototype.setMaterialPerSmoothingGroup = function(materialPerSmoothingGroup) { 49 | this.materialPerSmoothingGroup = materialPerSmoothingGroup === true; 50 | }; 51 | 52 | /** 53 | * Use this convenient method to load an OBJ file at the given URL. By default the fileLoader uses an arraybuffer. 54 | * @memberOf THREE.OBJLoader2 55 | * 56 | * @param {string} url A string containing the path/URL of the .obj file. 57 | * @param {callback} onLoad A function to be called after loading is successfully completed. The function receives loaded Object3D as an argument. 58 | * @param {callback} [onProgress] A function to be called while the loading is in progress. The argument will be the XMLHttpRequest instance, which contains total and Integer bytes. 59 | * @param {callback} [onError] A function to be called if an error occurs during loading. The function receives the error as an argument. 60 | * @param {callback} [onMeshAlter] A function to be called after a new mesh raw data becomes available for alteration. 61 | * @param {boolean} [useAsync] If true, uses async loading with worker, if false loads data synchronously. 62 | */ 63 | OBJLoader2.prototype.load = function(url, onLoad, onProgress, onError, onMeshAlter, useAsync) { 64 | var scope = this; 65 | if (!Validator.isValid(onProgress)) { 66 | var numericalValueRef = 0; 67 | var numericalValue = 0; 68 | onProgress = function(event) { 69 | if (!event.lengthComputable) return; 70 | 71 | numericalValue = event.loaded / event.total; 72 | if (numericalValue > numericalValueRef) { 73 | 74 | numericalValueRef = numericalValue; 75 | var output = 'Download of "' + url + '": ' + (numericalValue * 100).toFixed(2) + '%'; 76 | scope.onProgress('progressLoad', output, numericalValue); 77 | 78 | } 79 | }; 80 | } 81 | 82 | if (!Validator.isValid(onError)) { 83 | onError = function(event) { 84 | var output = 'Error occurred while downloading "' + url + '"'; 85 | scope.logger.logError(output + ': ' + event); 86 | scope.onProgress('error', output, -1); 87 | }; 88 | } 89 | 90 | this.fileLoader.setPath(this.path); 91 | this.fileLoader.setResponseType('arraybuffer'); 92 | this.fileLoader.load(url, function(content) { 93 | if (useAsync) { 94 | 95 | scope.parseAsync(content, onLoad); 96 | 97 | } else { 98 | 99 | var callbacks = new THREE.LoaderSupport.Callbacks(); 100 | callbacks.setCallbackOnMeshAlter(onMeshAlter); 101 | scope._setCallbacks(callbacks); 102 | onLoad({ 103 | detail: { 104 | loaderRootNode: scope.parse(content), 105 | modelName: scope.modelName, 106 | instanceNo: scope.instanceNo 107 | } 108 | }); 109 | 110 | } 111 | 112 | }, onProgress, onError); 113 | 114 | }; 115 | 116 | /** 117 | * Run the loader according the provided instructions. 118 | * @memberOf THREE.OBJLoader2 119 | * 120 | * @param {THREE.LoaderSupport.PrepData} prepData All parameters and resources required for execution 121 | * @param {THREE.LoaderSupport.WorkerSupport} [workerSupportExternal] Use pre-existing WorkerSupport 122 | */ 123 | OBJLoader2.prototype.run = function(prepData, workerSupportExternal) { 124 | this._applyPrepData(prepData); 125 | var available = this._checkFiles(prepData.resources); 126 | if (Validator.isValid(workerSupportExternal)) { 127 | 128 | this.terminateWorkerOnLoad = false; 129 | this.workerSupport = workerSupportExternal; 130 | this.logger = workerSupportExternal.logger; 131 | 132 | } 133 | var scope = this; 134 | var onMaterialsLoaded = function(materials) { 135 | scope.builder.setMaterials(materials); 136 | 137 | if (Validator.isValid(available.obj.content)) { 138 | 139 | if (prepData.useAsync) { 140 | 141 | scope.parseAsync(available.obj.content, scope.callbacks.onLoad); 142 | 143 | } else { 144 | 145 | scope.parse(available.obj.content); 146 | 147 | } 148 | } else { 149 | 150 | scope.setPath(available.obj.path); 151 | scope.load(available.obj.name, scope.callbacks.onLoad, null, null, scope.callbacks.onMeshAlter, prepData.useAsync); 152 | 153 | } 154 | }; 155 | 156 | this._loadMtl(available.mtl, onMaterialsLoaded, prepData.crossOrigin); 157 | }; 158 | 159 | OBJLoader2.prototype._applyPrepData = function(prepData) { 160 | THREE.LoaderSupport.LoaderBase.prototype._applyPrepData.call(this, prepData); 161 | 162 | if (Validator.isValid(prepData)) { 163 | 164 | this.setMaterialPerSmoothingGroup(prepData.materialPerSmoothingGroup); 165 | 166 | } 167 | }; 168 | 169 | /** 170 | * Parses OBJ data synchronously from arraybuffer or string. 171 | * @memberOf THREE.OBJLoader2 172 | * 173 | * @param {arraybuffer|string} content OBJ data as Uint8Array or String 174 | */ 175 | OBJLoader2.prototype.parse = function(content) { 176 | this.logger.logTimeStart('OBJLoader2 parse: ' + this.modelName); 177 | 178 | var parser = new Parser(); 179 | parser.setLogConfig(this.logger.enabled, this.logger.debug); 180 | parser.setMaterialPerSmoothingGroup(this.materialPerSmoothingGroup); 181 | parser.setUseIndices(this.useIndices); 182 | parser.setDisregardNormals(this.disregardNormals); 183 | // sync code works directly on the material references 184 | parser.setMaterials(this.builder.getMaterials()); 185 | 186 | var scope = this; 187 | var onMeshLoaded = function(payload) { 188 | var meshes = scope.builder.processPayload(payload); 189 | var mesh; 190 | for (var i in meshes) { 191 | mesh = meshes[i]; 192 | scope.loaderRootNode.add(mesh); 193 | } 194 | }; 195 | parser.setCallbackBuilder(onMeshLoaded); 196 | var onProgressScoped = function(text, numericalValue) { 197 | scope.onProgress('progressParse', text, numericalValue); 198 | }; 199 | parser.setCallbackProgress(onProgressScoped); 200 | 201 | if (content instanceof ArrayBuffer || content instanceof Uint8Array) { 202 | 203 | this.logger.logInfo('Parsing arrayBuffer...'); 204 | parser.parse(content); 205 | 206 | } else if (typeof(content) === 'string' || content instanceof String) { 207 | 208 | this.logger.logInfo('Parsing text...'); 209 | parser.parseText(content); 210 | 211 | } else { 212 | 213 | throw 'Provided content was neither of type String nor Uint8Array! Aborting...'; 214 | 215 | } 216 | this.logger.logTimeEnd('OBJLoader2 parse: ' + this.modelName); 217 | 218 | return this.loaderRootNode; 219 | }; 220 | 221 | /** 222 | * Parses OBJ content asynchronously from arraybuffer. 223 | * @memberOf THREE.OBJLoader2 224 | * 225 | * @param {arraybuffer} content OBJ data as Uint8Array 226 | * @param {callback} onLoad Called after worker successfully completed loading 227 | */ 228 | OBJLoader2.prototype.parseAsync = function(content, onLoad) { 229 | this.logger.logTimeStart('OBJLoader2 parseAsync: ' + this.modelName); 230 | 231 | var scope = this; 232 | var scopedOnLoad = function() { 233 | onLoad({ 234 | detail: { 235 | loaderRootNode: scope.loaderRootNode, 236 | modelName: scope.modelName, 237 | instanceNo: scope.instanceNo 238 | } 239 | }); 240 | scope.logger.logTimeEnd('OBJLoader2 parseAsync: ' + scope.modelName); 241 | }; 242 | var scopedOnMeshLoaded = function(payload) { 243 | var meshes = scope.builder.processPayload(payload); 244 | var mesh; 245 | for (var i in meshes) { 246 | mesh = meshes[i]; 247 | scope.loaderRootNode.add(mesh); 248 | } 249 | }; 250 | 251 | this.workerSupport = Validator.verifyInput(this.workerSupport, new THREE.LoaderSupport.WorkerSupport(this.logger)); 252 | var buildCode = function(funcBuildObject, funcBuildSingelton) { 253 | var workerCode = ''; 254 | workerCode += '/**\n'; 255 | workerCode += ' * This code was constructed by OBJLoader2 buildCode.\n'; 256 | workerCode += ' */\n\n'; 257 | workerCode += funcBuildObject('Validator', Validator); 258 | workerCode += funcBuildSingelton('ConsoleLogger', 'ConsoleLogger', ConsoleLogger); 259 | workerCode += funcBuildSingelton('LoaderBase', 'LoaderBase', LoaderBase); 260 | workerCode += funcBuildObject('Consts', Consts); 261 | workerCode += funcBuildSingelton('Parser', 'Parser', Parser); 262 | workerCode += funcBuildSingelton('RawMesh', 'RawMesh', RawMesh); 263 | workerCode += funcBuildSingelton('RawMeshSubGroup', 'RawMeshSubGroup', RawMeshSubGroup); 264 | 265 | return workerCode; 266 | }; 267 | this.workerSupport.validate(buildCode, false); 268 | this.workerSupport.setCallbacks(scopedOnMeshLoaded, scopedOnLoad); 269 | if (scope.terminateWorkerOnLoad) this.workerSupport.setTerminateRequested(true); 270 | 271 | var materialNames = {}; 272 | var materials = this.builder.getMaterials(); 273 | for (var materialName in materials) { 274 | 275 | materialNames[materialName] = materialName; 276 | 277 | } 278 | this.workerSupport.run({ 279 | params: { 280 | useAsync: true, 281 | materialPerSmoothingGroup: this.materialPerSmoothingGroup, 282 | useIndices: this.useIndices, 283 | disregardNormals: this.disregardNormals 284 | }, 285 | logger: { 286 | debug: this.logger.debug, 287 | enabled: this.logger.enabled 288 | }, 289 | materials: { 290 | // in async case only material names are supplied to parser 291 | materials: materialNames 292 | }, 293 | data: { 294 | input: content, 295 | options: null 296 | } 297 | }); 298 | }; 299 | 300 | /** 301 | * Constants used by THREE.OBJLoader2 302 | */ 303 | var Consts = { 304 | CODE_LF: 10, 305 | CODE_CR: 13, 306 | CODE_SPACE: 32, 307 | CODE_SLASH: 47, 308 | STRING_LF: '\n', 309 | STRING_CR: '\r', 310 | STRING_SPACE: ' ', 311 | STRING_SLASH: '/', 312 | LINE_F: 'f', 313 | LINE_G: 'g', 314 | LINE_L: 'l', 315 | LINE_O: 'o', 316 | LINE_S: 's', 317 | LINE_V: 'v', 318 | LINE_VT: 'vt', 319 | LINE_VN: 'vn', 320 | LINE_MTLLIB: 'mtllib', 321 | LINE_USEMTL: 'usemtl' 322 | }; 323 | 324 | /** 325 | * Parse OBJ data either from ArrayBuffer or string 326 | * @class 327 | */ 328 | var Parser = (function() { 329 | 330 | var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; 331 | 332 | function Parser() { 333 | this.callbackProgress = null; 334 | this.callbackBuilder = null; 335 | 336 | this.materials = {}; 337 | this.rawMesh = null; 338 | this.useAsync = false; 339 | this.materialPerSmoothingGroup = false; 340 | this.useIndices = false; 341 | this.disregardNormals = false; 342 | 343 | this.inputObjectCount = 1; 344 | this.outputObjectCount = 1; 345 | this.counts = { 346 | vertices: 0, 347 | faces: 0, 348 | doubleIndicesCount: 0 349 | }; 350 | this.logger = new ConsoleLogger(); 351 | this.totalBytes = 0; 352 | }; 353 | 354 | Parser.prototype.setUseAsync = function(useAsync) { 355 | this.useAsync = useAsync; 356 | }; 357 | 358 | Parser.prototype.setMaterialPerSmoothingGroup = function(materialPerSmoothingGroup) { 359 | this.materialPerSmoothingGroup = materialPerSmoothingGroup; 360 | }; 361 | 362 | Parser.prototype.setUseIndices = function(useIndices) { 363 | this.useIndices = useIndices; 364 | }; 365 | 366 | Parser.prototype.setDisregardNormals = function(disregardNormals) { 367 | this.disregardNormals = disregardNormals; 368 | }; 369 | 370 | Parser.prototype.setMaterials = function(materials) { 371 | this.materials = Validator.verifyInput(materials, this.materials); 372 | this.materials = Validator.verifyInput(this.materials, {}); 373 | }; 374 | 375 | Parser.prototype.setCallbackBuilder = function(callbackBuilder) { 376 | this.callbackBuilder = callbackBuilder; 377 | if (!Validator.isValid(this.callbackBuilder)) throw 'Unable to run as no "builder" callback is set.'; 378 | }; 379 | 380 | Parser.prototype.setCallbackProgress = function(callbackProgress) { 381 | this.callbackProgress = callbackProgress; 382 | }; 383 | 384 | Parser.prototype.setLogConfig = function(enabled, debug) { 385 | this.logger.setEnabled(enabled); 386 | this.logger.setDebug(debug); 387 | }; 388 | 389 | Parser.prototype.configure = function() { 390 | this.rawMesh = new RawMesh(this.materialPerSmoothingGroup, this.useIndices, this.disregardNormals); 391 | 392 | if (this.logger.isEnabled()) { 393 | 394 | var matKeys = Object.keys(this.materials); 395 | var matNames = (matKeys.length > 0) ? '\n\tmaterialNames:\n\t\t- ' + matKeys.join('\n\t\t- ') : '\n\tmaterialNames: None'; 396 | var printedConfig = 'OBJLoader2.Parser configuration:' + 397 | matNames + 398 | '\n\tuseAsync: ' + this.useAsync + 399 | '\n\tmaterialPerSmoothingGroup: ' + this.materialPerSmoothingGroup + 400 | '\n\tuseIndices: ' + this.useIndices + 401 | '\n\tdisregardNormals: ' + this.disregardNormals + 402 | '\n\tcallbackBuilderName: ' + this.callbackBuilder.name + 403 | '\n\tcallbackProgressName: ' + this.callbackProgress.name; 404 | this.logger.logInfo(printedConfig); 405 | } 406 | }; 407 | 408 | /** 409 | * Parse the provided arraybuffer 410 | * @memberOf Parser 411 | * 412 | * @param {Uint8Array} arrayBuffer OBJ data as Uint8Array 413 | */ 414 | Parser.prototype.parse = function(arrayBuffer) { 415 | this.logger.logTimeStart('OBJLoader2.Parser.parse'); 416 | this.configure(); 417 | 418 | var arrayBufferView = new Uint8Array(arrayBuffer); 419 | var length = arrayBufferView.byteLength; 420 | this.totalBytes = length; 421 | var buffer = new Array(128); 422 | var bufferPointer = 0; 423 | var slashSpacePattern = new Array(16); 424 | var slashSpacePatternPointer = 0; 425 | var code; 426 | var word = ''; 427 | var i = 0; 428 | for (; i < length; i++) { 429 | 430 | code = arrayBufferView[i]; 431 | switch (code) { 432 | case Consts.CODE_SPACE: 433 | if (word.length > 0) buffer[bufferPointer++] = word; 434 | slashSpacePattern[slashSpacePatternPointer++] = 0; 435 | word = ''; 436 | break; 437 | 438 | case Consts.CODE_SLASH: 439 | if (word.length > 0) buffer[bufferPointer++] = word; 440 | slashSpacePattern[slashSpacePatternPointer++] = 1; 441 | word = ''; 442 | break; 443 | 444 | case Consts.CODE_LF: 445 | if (word.length > 0) buffer[bufferPointer++] = word; 446 | word = ''; 447 | this.processLine(buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i); 448 | bufferPointer = 0; 449 | slashSpacePatternPointer = 0; 450 | break; 451 | 452 | case Consts.CODE_CR: 453 | break; 454 | 455 | default: 456 | word += String.fromCharCode(code); 457 | break; 458 | } 459 | } 460 | this.finalize(i); 461 | this.logger.logTimeEnd('OBJLoader2.Parser.parse'); 462 | }; 463 | 464 | /** 465 | * Parse the provided text 466 | * @memberOf Parser 467 | * 468 | * @param {string} text OBJ data as string 469 | */ 470 | Parser.prototype.parseText = function(text) { 471 | this.logger.logTimeStart('OBJLoader2.Parser.parseText'); 472 | this.configure(); 473 | 474 | var length = text.length; 475 | this.totalBytes = length; 476 | var buffer = new Array(128); 477 | var bufferPointer = 0; 478 | var slashSpacePattern = new Array(16); 479 | var slashSpacePatternPointer = 0; 480 | var char; 481 | var word = ''; 482 | var i = 0; 483 | for (; i < length; i++) { 484 | 485 | char = text[i]; 486 | switch (char) { 487 | case Consts.STRING_SPACE: 488 | if (word.length > 0) buffer[bufferPointer++] = word; 489 | slashSpacePattern[slashSpacePatternPointer++] = 0; 490 | word = ''; 491 | break; 492 | 493 | case Consts.STRING_SLASH: 494 | if (word.length > 0) buffer[bufferPointer++] = word; 495 | slashSpacePattern[slashSpacePatternPointer++] = 1; 496 | word = ''; 497 | break; 498 | 499 | case Consts.STRING_LF: 500 | if (word.length > 0) buffer[bufferPointer++] = word; 501 | word = ''; 502 | this.processLine(buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, i); 503 | bufferPointer = 0; 504 | slashSpacePatternPointer = 0; 505 | break; 506 | 507 | case Consts.STRING_CR: 508 | break; 509 | 510 | default: 511 | word += char; 512 | } 513 | } 514 | this.finalize(i); 515 | this.logger.logTimeEnd('OBJLoader2.Parser.parseText'); 516 | }; 517 | 518 | Parser.prototype.processLine = function(buffer, bufferPointer, slashSpacePattern, slashSpacePatternPointer, currentByte) { 519 | if (bufferPointer < 1) return; 520 | 521 | var countSlashes = function(slashSpacePattern, slashSpacePatternPointer) { 522 | var slashesCount = 0; 523 | for (var i = 0; i < slashSpacePatternPointer; i++) { 524 | slashesCount += slashSpacePattern[i]; 525 | } 526 | return slashesCount; 527 | }; 528 | 529 | var concatStringBuffer = function(buffer, bufferPointer, slashSpacePattern) { 530 | var concatBuffer = ''; 531 | if (bufferPointer === 2) { 532 | 533 | concatBuffer = buffer[1]; 534 | 535 | } else { 536 | 537 | var bufferLength = bufferPointer - 1; 538 | for (var i = 1; i < bufferLength; i++) { 539 | 540 | concatBuffer += buffer[i] + (slashSpacePattern[i] === 0 ? ' ' : '/'); 541 | 542 | } 543 | concatBuffer += buffer[bufferLength]; 544 | 545 | } 546 | return concatBuffer; 547 | }; 548 | 549 | var flushStringBuffer = function(buffer, bufferPointer) { 550 | for (var i = 0; i < bufferPointer; i++) { 551 | buffer[i] = ''; 552 | } 553 | }; 554 | 555 | switch (buffer[0]) { 556 | case Consts.LINE_V: 557 | this.rawMesh.pushVertex(buffer, bufferPointer > 4); 558 | break; 559 | 560 | case Consts.LINE_VT: 561 | this.rawMesh.pushUv(buffer); 562 | break; 563 | 564 | case Consts.LINE_VN: 565 | this.rawMesh.pushNormal(buffer); 566 | break; 567 | 568 | case Consts.LINE_F: 569 | this.rawMesh.processFaces(buffer, bufferPointer, countSlashes(slashSpacePattern, slashSpacePatternPointer)); 570 | break; 571 | 572 | case Consts.LINE_L: 573 | this.rawMesh.processLines(buffer, bufferPointer, countSlashes(slashSpacePattern, slashSpacePatternPointer)); 574 | break; 575 | 576 | case Consts.LINE_S: 577 | this.rawMesh.pushSmoothingGroup(buffer[1]); 578 | flushStringBuffer(buffer, bufferPointer); 579 | break; 580 | 581 | case Consts.LINE_G: 582 | // 'g' leads to creation of mesh if valid data (faces declaration was done before), otherwise only groupName gets set 583 | this.processCompletedMesh(currentByte); 584 | this.rawMesh.pushGroup(concatStringBuffer(buffer, bufferPointer, slashSpacePattern)); 585 | flushStringBuffer(buffer, bufferPointer); 586 | break; 587 | 588 | case Consts.LINE_O: 589 | // 'o' is pure meta-information and does not result in creation of new meshes 590 | this.rawMesh.pushObject(concatStringBuffer(buffer, bufferPointer, slashSpacePattern)); 591 | flushStringBuffer(buffer, bufferPointer); 592 | break; 593 | 594 | case Consts.LINE_MTLLIB: 595 | this.rawMesh.pushMtllib(concatStringBuffer(buffer, bufferPointer, slashSpacePattern)); 596 | flushStringBuffer(buffer, bufferPointer); 597 | break; 598 | 599 | case Consts.LINE_USEMTL: 600 | this.rawMesh.pushUsemtl(concatStringBuffer(buffer, bufferPointer, slashSpacePattern)); 601 | flushStringBuffer(buffer, bufferPointer); 602 | break; 603 | 604 | default: 605 | break; 606 | } 607 | }; 608 | 609 | Parser.prototype.createRawMeshReport = function(rawMesh, inputObjectCount) { 610 | var report = rawMesh.createReport(inputObjectCount); 611 | return 'Input Object number: ' + inputObjectCount + 612 | '\n\tObject name: ' + report.objectName + 613 | '\n\tGroup name: ' + report.groupName + 614 | '\n\tMtllib name: ' + report.mtllibName + 615 | '\n\tVertex count: ' + report.vertexCount + 616 | '\n\tNormal count: ' + report.normalCount + 617 | '\n\tUV count: ' + report.uvCount + 618 | '\n\tSmoothingGroup count: ' + report.smoothingGroupCount + 619 | '\n\tMaterial count: ' + report.mtlCount + 620 | '\n\tReal RawMeshSubGroup count: ' + report.subGroups; 621 | }; 622 | 623 | Parser.prototype.processCompletedMesh = function(currentByte) { 624 | var result = this.rawMesh.finalize(); 625 | if (Validator.isValid(result)) { 626 | 627 | if (this.rawMesh.colors.length > 0 && this.rawMesh.colors.length !== this.rawMesh.vertices.length) { 628 | 629 | throw 'Vertex Colors were detected, but vertex count and color count do not match!'; 630 | 631 | } 632 | if (this.logger.isDebug()) this.logger.logDebug(this.createRawMeshReport(this.rawMesh, this.inputObjectCount)); 633 | this.inputObjectCount++; 634 | 635 | this.buildMesh(result, currentByte); 636 | var progressBytesPercent = currentByte / this.totalBytes; 637 | this.callbackProgress('Completed [o: ' + this.rawMesh.objectName + ' g:' + this.rawMesh.groupName + '] Total progress: ' + (progressBytesPercent * 100).toFixed(2) + '%', progressBytesPercent); 638 | this.rawMesh.reset(this.rawMesh.smoothingGroup.splitMaterials); 639 | 640 | return true; 641 | 642 | } else { 643 | 644 | return false; 645 | } 646 | }; 647 | 648 | Parser.prototype.finalize = function(currentByte) { 649 | this.logger.logInfo('Global output object count: ' + this.outputObjectCount); 650 | if (this.processCompletedMesh(currentByte) && this.logger.isEnabled()) { 651 | 652 | var parserFinalReport = 'Overall counts: ' + 653 | '\n\tVertices: ' + this.counts.vertices + 654 | '\n\tFaces: ' + this.counts.faces + 655 | '\n\tMultiple definitions: ' + this.counts.doubleIndicesCount; 656 | this.logger.logInfo(parserFinalReport); 657 | 658 | } 659 | }; 660 | 661 | /** 662 | * RawObjectDescriptions are transformed to too intermediate format that is forwarded to the Builder. 663 | * It is ensured that rawObjectDescriptions only contain objects with vertices (no need to check). 664 | * 665 | * @param result 666 | */ 667 | Parser.prototype.buildMesh = function(result, currentByte) { 668 | var rawObjectDescriptions = result.subGroups; 669 | 670 | var vertexFA = new Float32Array(result.absoluteVertexCount); 671 | this.counts.vertices += result.absoluteVertexCount / 3; 672 | this.counts.faces += result.faceCount; 673 | this.counts.doubleIndicesCount += result.doubleIndicesCount; 674 | var indexUA = (result.absoluteIndexCount > 0) ? new Uint32Array(result.absoluteIndexCount) : null; 675 | var colorFA = (result.absoluteColorCount > 0) ? new Float32Array(result.absoluteColorCount) : null; 676 | var normalFA = (result.absoluteNormalCount > 0) ? new Float32Array(result.absoluteNormalCount) : null; 677 | var uvFA = (result.absoluteUvCount > 0) ? new Float32Array(result.absoluteUvCount) : null; 678 | var haveVertexColors = Validator.isValid(colorFA); 679 | 680 | var rawObjectDescription; 681 | var materialNames = []; 682 | 683 | var createMultiMaterial = (rawObjectDescriptions.length > 1); 684 | var materialIndex = 0; 685 | var materialIndexMapping = []; 686 | var selectedMaterialIndex; 687 | var materialGroup; 688 | var materialGroups = []; 689 | 690 | var vertexFAOffset = 0; 691 | var indexUAOffset = 0; 692 | var colorFAOffset = 0; 693 | var normalFAOffset = 0; 694 | var uvFAOffset = 0; 695 | var materialGroupOffset = 0; 696 | var materialGroupLength = 0; 697 | 698 | var materialOrg, material, materialName, materialNameOrg; 699 | for (var oodIndex in rawObjectDescriptions) { 700 | 701 | if (!rawObjectDescriptions.hasOwnProperty(oodIndex)) continue; 702 | rawObjectDescription = rawObjectDescriptions[oodIndex]; 703 | 704 | materialNameOrg = rawObjectDescription.materialName; 705 | materialName = materialNameOrg + (haveVertexColors ? '_vertexColor' : '') + (rawObjectDescription.smoothingGroup === 0 ? '_flat' : ''); 706 | materialOrg = this.materials[materialNameOrg]; 707 | material = this.materials[materialName]; 708 | 709 | // both original and derived names do not lead to an existing material => need to use a default material 710 | if (!Validator.isValid(materialOrg) && !Validator.isValid(material)) { 711 | 712 | var defaultMaterialName = haveVertexColors ? 'vertexColorMaterial' : 'defaultMaterial'; 713 | materialOrg = this.materials[defaultMaterialName]; 714 | this.logger.logWarn('object_group "' + rawObjectDescription.objectName + '_' + 715 | rawObjectDescription.groupName + '" was defined with unresolvable material "' + 716 | materialNameOrg + '"! Assigning "' + defaultMaterialName + '".'); 717 | materialNameOrg = defaultMaterialName; 718 | 719 | // if names are identical then there is no need for later manipulation 720 | if (materialNameOrg === materialName) { 721 | 722 | material = materialOrg; 723 | materialName = defaultMaterialName; 724 | 725 | } 726 | 727 | } 728 | if (!Validator.isValid(material)) { 729 | 730 | var materialCloneInstructions = { 731 | materialNameOrg: materialNameOrg, 732 | materialName: materialName, 733 | materialProperties: { 734 | vertexColors: haveVertexColors ? 2 : 0, 735 | flatShading: rawObjectDescription.smoothingGroup === 0 736 | } 737 | }; 738 | var payload = { 739 | cmd: 'materialData', 740 | materials: { 741 | materialCloneInstructions: materialCloneInstructions 742 | } 743 | }; 744 | this.callbackBuilder(payload); 745 | 746 | // fake entry for async; sync Parser always works on material references (Builder update directly visible here) 747 | if (this.useAsync) this.materials[materialName] = materialCloneInstructions; 748 | 749 | } 750 | 751 | if (createMultiMaterial) { 752 | 753 | // re-use material if already used before. Reduces materials array size and eliminates duplicates 754 | selectedMaterialIndex = materialIndexMapping[materialName]; 755 | if (!selectedMaterialIndex) { 756 | 757 | selectedMaterialIndex = materialIndex; 758 | materialIndexMapping[materialName] = materialIndex; 759 | materialNames.push(materialName); 760 | materialIndex++; 761 | 762 | } 763 | materialGroupLength = this.useIndices ? rawObjectDescription.indices.length : rawObjectDescription.vertices.length / 3; 764 | materialGroup = { 765 | start: materialGroupOffset, 766 | count: materialGroupLength, 767 | index: selectedMaterialIndex 768 | }; 769 | materialGroups.push(materialGroup); 770 | materialGroupOffset += materialGroupLength; 771 | 772 | } else { 773 | 774 | materialNames.push(materialName); 775 | 776 | } 777 | 778 | vertexFA.set(rawObjectDescription.vertices, vertexFAOffset); 779 | vertexFAOffset += rawObjectDescription.vertices.length; 780 | 781 | if (indexUA) { 782 | 783 | indexUA.set(rawObjectDescription.indices, indexUAOffset); 784 | indexUAOffset += rawObjectDescription.indices.length; 785 | 786 | } 787 | 788 | if (colorFA) { 789 | 790 | colorFA.set(rawObjectDescription.colors, colorFAOffset); 791 | colorFAOffset += rawObjectDescription.colors.length; 792 | 793 | } 794 | 795 | if (normalFA) { 796 | 797 | normalFA.set(rawObjectDescription.normals, normalFAOffset); 798 | normalFAOffset += rawObjectDescription.normals.length; 799 | 800 | } 801 | if (uvFA) { 802 | 803 | uvFA.set(rawObjectDescription.uvs, uvFAOffset); 804 | uvFAOffset += rawObjectDescription.uvs.length; 805 | 806 | } 807 | 808 | if (this.logger.isDebug()) { 809 | var materialIndexLine = Validator.isValid(selectedMaterialIndex) ? '\n\t\tmaterialIndex: ' + selectedMaterialIndex : ''; 810 | var createdReport = 'Output Object no.: ' + this.outputObjectCount + 811 | '\n\t\tgroupName: ' + rawObjectDescription.groupName + 812 | materialIndexLine + 813 | '\n\t\tmaterialName: ' + rawObjectDescription.materialName + 814 | '\n\t\tsmoothingGroup: ' + rawObjectDescription.smoothingGroup + 815 | '\n\t\tobjectName: ' + rawObjectDescription.objectName + 816 | '\n\t\t#vertices: ' + rawObjectDescription.vertices.length / 3 + 817 | '\n\t\t#indices: ' + rawObjectDescription.indices.length + 818 | '\n\t\t#colors: ' + rawObjectDescription.colors.length / 3 + 819 | '\n\t\t#uvs: ' + rawObjectDescription.uvs.length / 2 + 820 | '\n\t\t#normals: ' + rawObjectDescription.normals.length / 3; 821 | this.logger.logDebug(createdReport); 822 | } 823 | 824 | } 825 | 826 | this.outputObjectCount++; 827 | this.callbackBuilder({ 828 | cmd: 'meshData', 829 | progress: { 830 | numericalValue: currentByte / this.totalBytes 831 | }, 832 | params: { 833 | meshName: result.name 834 | }, 835 | materials: { 836 | multiMaterial: createMultiMaterial, 837 | materialNames: materialNames, 838 | materialGroups: materialGroups 839 | }, 840 | buffers: { 841 | vertices: vertexFA, 842 | indices: indexUA, 843 | colors: colorFA, 844 | normals: normalFA, 845 | uvs: uvFA 846 | } 847 | }, [vertexFA.buffer], 848 | Validator.isValid(indexUA) ? [indexUA.buffer] : null, 849 | Validator.isValid(colorFA) ? [colorFA.buffer] : null, 850 | Validator.isValid(normalFA) ? [normalFA.buffer] : null, 851 | Validator.isValid(uvFA) ? [uvFA.buffer] : null 852 | ); 853 | }; 854 | 855 | return Parser; 856 | })(); 857 | 858 | /** 859 | * {@link RawMesh} is only used by {@link Parser}. 860 | * The user of OBJLoader2 does not need to care about this class. 861 | * It is defined publicly for inclusion in web worker based OBJ loader ({@link THREE.OBJLoader2.WWOBJLoader2}) 862 | */ 863 | var RawMesh = (function() { 864 | 865 | function RawMesh(materialPerSmoothingGroup, useIndices, disregardNormals) { 866 | this.vertices = []; 867 | this.colors = []; 868 | this.normals = []; 869 | this.uvs = []; 870 | 871 | this.useIndices = useIndices === true; 872 | this.disregardNormals = disregardNormals === true; 873 | 874 | this.objectName = ''; 875 | this.groupName = ''; 876 | this.activeMtlName = ''; 877 | this.mtllibName = ''; 878 | this.reset(materialPerSmoothingGroup); 879 | } 880 | 881 | RawMesh.prototype.reset = function(materialPerSmoothingGroup) { 882 | // faces are stored according combined index of group, material and smoothingGroup (0 or not) 883 | this.subGroups = []; 884 | this.subGroupInUse = null; 885 | this.smoothingGroup = { 886 | splitMaterials: materialPerSmoothingGroup === true, 887 | normalized: -1, 888 | real: -1 889 | }; 890 | // this default index is required as it is possible to define faces without 'g' or 'usemtl' 891 | this.pushSmoothingGroup(1); 892 | 893 | this.doubleIndicesCount = 0; 894 | this.faceCount = 0; 895 | this.mtlCount = 0; 896 | this.smoothingGroupCount = 0; 897 | }; 898 | 899 | RawMesh.prototype.pushVertex = function(buffer, haveVertexColors) { 900 | this.vertices.push(parseFloat(buffer[1])); 901 | this.vertices.push(parseFloat(buffer[2])); 902 | this.vertices.push(parseFloat(buffer[3])); 903 | if (haveVertexColors) { 904 | 905 | this.colors.push(parseFloat(buffer[4])); 906 | this.colors.push(parseFloat(buffer[5])); 907 | this.colors.push(parseFloat(buffer[6])); 908 | 909 | } 910 | }; 911 | 912 | RawMesh.prototype.pushUv = function(buffer) { 913 | this.uvs.push(parseFloat(buffer[1])); 914 | this.uvs.push(parseFloat(buffer[2])); 915 | }; 916 | 917 | RawMesh.prototype.pushNormal = function(buffer) { 918 | this.normals.push(parseFloat(buffer[1])); 919 | this.normals.push(parseFloat(buffer[2])); 920 | this.normals.push(parseFloat(buffer[3])); 921 | }; 922 | 923 | RawMesh.prototype.pushGroup = function(groupName) { 924 | this.groupName = Validator.verifyInput(groupName, ''); 925 | }; 926 | 927 | RawMesh.prototype.pushObject = function(objectName) { 928 | this.objectName = Validator.verifyInput(objectName, ''); 929 | }; 930 | 931 | RawMesh.prototype.pushMtllib = function(mtllibName) { 932 | this.mtllibName = Validator.verifyInput(mtllibName, ''); 933 | }; 934 | 935 | RawMesh.prototype.pushUsemtl = function(mtlName) { 936 | if (this.activeMtlName === mtlName || !Validator.isValid(mtlName)) return; 937 | this.activeMtlName = mtlName; 938 | this.mtlCount++; 939 | 940 | this.verifyIndex(); 941 | }; 942 | 943 | RawMesh.prototype.pushSmoothingGroup = function(smoothingGroup) { 944 | var smoothingGroupInt = parseInt(smoothingGroup); 945 | if (isNaN(smoothingGroupInt)) { 946 | smoothingGroupInt = smoothingGroup === "off" ? 0 : 1; 947 | } 948 | 949 | var smoothCheck = this.smoothingGroup.normalized; 950 | this.smoothingGroup.normalized = this.smoothingGroup.splitMaterials ? smoothingGroupInt : (smoothingGroupInt === 0) ? 0 : 1; 951 | this.smoothingGroup.real = smoothingGroupInt; 952 | 953 | if (smoothCheck !== smoothingGroupInt) { 954 | 955 | this.smoothingGroupCount++; 956 | this.verifyIndex(); 957 | 958 | } 959 | }; 960 | 961 | RawMesh.prototype.verifyIndex = function() { 962 | var index = this.activeMtlName + '|' + this.smoothingGroup.normalized; 963 | this.subGroupInUse = this.subGroups[index]; 964 | if (!Validator.isValid(this.subGroupInUse)) { 965 | 966 | this.subGroupInUse = new RawMeshSubGroup(this.objectName, this.groupName, this.activeMtlName, this.smoothingGroup.normalized); 967 | this.subGroups[index] = this.subGroupInUse; 968 | 969 | } 970 | }; 971 | 972 | RawMesh.prototype.processFaces = function(buffer, bufferPointer, slashesCount) { 973 | var bufferLength = bufferPointer - 1; 974 | var i, length; 975 | 976 | // "f vertex ..." 977 | if (slashesCount === 0) { 978 | 979 | for (i = 2, length = bufferLength; i < length; i++) { 980 | 981 | this.buildFace(buffer[1]); 982 | this.buildFace(buffer[i]); 983 | this.buildFace(buffer[i + 1]); 984 | 985 | } 986 | 987 | // "f vertex/uv ..." 988 | } else if (bufferLength === slashesCount * 2) { 989 | 990 | for (i = 3, length = bufferLength - 2; i < length; i += 2) { 991 | 992 | this.buildFace(buffer[1], buffer[2]); 993 | this.buildFace(buffer[i], buffer[i + 1]); 994 | this.buildFace(buffer[i + 2], buffer[i + 3]); 995 | 996 | } 997 | 998 | // "f vertex/uv/normal ..." 999 | } else if (bufferLength * 2 === slashesCount * 3) { 1000 | 1001 | for (i = 4, length = bufferLength - 3; i < length; i += 3) { 1002 | 1003 | this.buildFace(buffer[1], buffer[2], buffer[3]); 1004 | this.buildFace(buffer[i], buffer[i + 1], buffer[i + 2]); 1005 | this.buildFace(buffer[i + 3], buffer[i + 4], buffer[i + 5]); 1006 | 1007 | } 1008 | 1009 | // "f vertex//normal ..." 1010 | } else { 1011 | 1012 | for (i = 3, length = bufferLength - 2; i < length; i += 2) { 1013 | 1014 | this.buildFace(buffer[1], undefined, buffer[2]); 1015 | this.buildFace(buffer[i], undefined, buffer[i + 1]); 1016 | this.buildFace(buffer[i + 2], undefined, buffer[i + 3]); 1017 | 1018 | } 1019 | 1020 | } 1021 | }; 1022 | 1023 | 1024 | RawMesh.prototype.buildFace = function(faceIndexV, faceIndexU, faceIndexN) { 1025 | var sgiu = this.subGroupInUse; 1026 | if (this.disregardNormals) faceIndexN = undefined; 1027 | var scope = this; 1028 | var updateRawObjectDescriptionInUse = function() { 1029 | 1030 | var faceIndexVi = parseInt(faceIndexV); 1031 | var indexPointerV = 3 * (faceIndexVi > 0 ? faceIndexVi - 1 : faceIndexVi + scope.vertices.length / 3); 1032 | 1033 | var vertices = sgiu.vertices; 1034 | vertices.push(scope.vertices[indexPointerV++]); 1035 | vertices.push(scope.vertices[indexPointerV++]); 1036 | vertices.push(scope.vertices[indexPointerV]); 1037 | 1038 | var indexPointerC = scope.colors.length > 0 ? indexPointerV : null; 1039 | if (indexPointerC !== null) { 1040 | 1041 | var colors = sgiu.colors; 1042 | colors.push(scope.colors[indexPointerC++]); 1043 | colors.push(scope.colors[indexPointerC++]); 1044 | colors.push(scope.colors[indexPointerC]); 1045 | 1046 | } 1047 | 1048 | if (faceIndexU) { 1049 | 1050 | var faceIndexUi = parseInt(faceIndexU); 1051 | var indexPointerU = 2 * (faceIndexUi > 0 ? faceIndexUi - 1 : faceIndexUi + scope.uvs.length / 2); 1052 | var uvs = sgiu.uvs; 1053 | uvs.push(scope.uvs[indexPointerU++]); 1054 | uvs.push(scope.uvs[indexPointerU]); 1055 | 1056 | } 1057 | if (faceIndexN) { 1058 | 1059 | var faceIndexNi = parseInt(faceIndexN); 1060 | var indexPointerN = 3 * (faceIndexNi > 0 ? faceIndexNi - 1 : faceIndexNi + scope.normals.length / 3); 1061 | var normals = sgiu.normals; 1062 | normals.push(scope.normals[indexPointerN++]); 1063 | normals.push(scope.normals[indexPointerN++]); 1064 | normals.push(scope.normals[indexPointerN]); 1065 | 1066 | } 1067 | }; 1068 | 1069 | if (this.useIndices) { 1070 | 1071 | var mappingName = faceIndexV + (faceIndexU ? '_' + faceIndexU : '_n') + (faceIndexN ? '_' + faceIndexN : '_n'); 1072 | var indicesPointer = sgiu.indexMappings[mappingName]; 1073 | if (Validator.isValid(indicesPointer)) { 1074 | 1075 | this.doubleIndicesCount++; 1076 | 1077 | } else { 1078 | 1079 | indicesPointer = sgiu.vertices.length / 3; 1080 | updateRawObjectDescriptionInUse(); 1081 | sgiu.indexMappings[mappingName] = indicesPointer; 1082 | sgiu.indexMappingsCount++; 1083 | 1084 | } 1085 | sgiu.indices.push(indicesPointer); 1086 | 1087 | } else { 1088 | 1089 | updateRawObjectDescriptionInUse(); 1090 | 1091 | } 1092 | this.faceCount++; 1093 | }; 1094 | 1095 | /* 1096 | * Support for lines with or without texture. First element in indexArray is the line identification 1097 | * 0: "f vertex/uv vertex/uv ..." 1098 | * 1: "f vertex vertex ..." 1099 | */ 1100 | RawMesh.prototype.processLines = function(buffer, bufferPointer, slashCount) { 1101 | var i = 1; 1102 | var length; 1103 | var bufferLength = bufferPointer - 1; 1104 | 1105 | if (bufferLength === slashCount * 2) { 1106 | 1107 | for (length = bufferLength - 2; i < length; i += 2) { 1108 | 1109 | this.vertices.push(parseInt(buffer[i])); 1110 | this.uvs.push(parseInt(buffer[i + 1])); 1111 | 1112 | } 1113 | 1114 | } else { 1115 | 1116 | for (length = bufferLength - 1; i < length; i++) { 1117 | 1118 | this.vertices.push(parseInt(buffer[i])); 1119 | 1120 | } 1121 | 1122 | } 1123 | }; 1124 | 1125 | /** 1126 | * Clear any empty rawObjectDescription and calculate absolute vertex, normal and uv counts 1127 | */ 1128 | RawMesh.prototype.finalize = function() { 1129 | var rawObjectDescriptionsTemp = []; 1130 | var rawObjectDescription; 1131 | var absoluteVertexCount = 0; 1132 | var absoluteIndexMappingsCount = 0; 1133 | var absoluteIndexCount = 0; 1134 | var absoluteColorCount = 0; 1135 | var absoluteNormalCount = 0; 1136 | var absoluteUvCount = 0; 1137 | var indices; 1138 | for (var name in this.subGroups) { 1139 | 1140 | rawObjectDescription = this.subGroups[name]; 1141 | if (rawObjectDescription.vertices.length > 0) { 1142 | 1143 | indices = rawObjectDescription.indices; 1144 | if (indices.length > 0 && absoluteIndexMappingsCount > 0) { 1145 | 1146 | for (var i in indices) indices[i] = indices[i] + absoluteIndexMappingsCount; 1147 | 1148 | } 1149 | rawObjectDescriptionsTemp.push(rawObjectDescription); 1150 | absoluteVertexCount += rawObjectDescription.vertices.length; 1151 | absoluteIndexMappingsCount += rawObjectDescription.indexMappingsCount; 1152 | absoluteIndexCount += rawObjectDescription.indices.length; 1153 | absoluteColorCount += rawObjectDescription.colors.length; 1154 | absoluteUvCount += rawObjectDescription.uvs.length; 1155 | absoluteNormalCount += rawObjectDescription.normals.length; 1156 | 1157 | } 1158 | } 1159 | 1160 | // do not continue if no result 1161 | var result = null; 1162 | if (rawObjectDescriptionsTemp.length > 0) { 1163 | 1164 | result = { 1165 | name: this.groupName !== '' ? this.groupName : this.objectName, 1166 | subGroups: rawObjectDescriptionsTemp, 1167 | absoluteVertexCount: absoluteVertexCount, 1168 | absoluteIndexCount: absoluteIndexCount, 1169 | absoluteColorCount: absoluteColorCount, 1170 | absoluteNormalCount: absoluteNormalCount, 1171 | absoluteUvCount: absoluteUvCount, 1172 | faceCount: this.faceCount, 1173 | doubleIndicesCount: this.doubleIndicesCount 1174 | }; 1175 | 1176 | } 1177 | return result; 1178 | }; 1179 | 1180 | RawMesh.prototype.createReport = function() { 1181 | var report = { 1182 | objectName: this.objectName, 1183 | groupName: this.groupName, 1184 | mtllibName: this.mtllibName, 1185 | vertexCount: this.vertices.length / 3, 1186 | normalCount: this.normals.length / 3, 1187 | uvCount: this.uvs.length / 2, 1188 | smoothingGroupCount: this.smoothingGroupCount, 1189 | mtlCount: this.mtlCount, 1190 | subGroups: this.subGroups.length 1191 | }; 1192 | 1193 | return report; 1194 | }; 1195 | 1196 | return RawMesh; 1197 | })(); 1198 | 1199 | /** 1200 | * Descriptive information and data (vertices, normals, uvs) to passed on to mesh building function. 1201 | * @class 1202 | * 1203 | * @param {string} objectName Name of the mesh 1204 | * @param {string} groupName Name of the group 1205 | * @param {string} materialName Name of the material 1206 | * @param {number} smoothingGroup Normalized smoothingGroup (0: flat shading, 1: smooth shading) 1207 | */ 1208 | var RawMeshSubGroup = (function() { 1209 | 1210 | function RawMeshSubGroup(objectName, groupName, materialName, smoothingGroup) { 1211 | this.objectName = objectName; 1212 | this.groupName = groupName; 1213 | this.materialName = materialName; 1214 | this.smoothingGroup = smoothingGroup; 1215 | this._init(); 1216 | } 1217 | 1218 | RawMeshSubGroup.prototype._init = function() { 1219 | this.vertices = []; 1220 | this.indexMappingsCount = 0; 1221 | this.indexMappings = []; 1222 | this.indices = []; 1223 | this.colors = []; 1224 | this.uvs = []; 1225 | this.normals = []; 1226 | }; 1227 | 1228 | return RawMeshSubGroup; 1229 | })(); 1230 | 1231 | OBJLoader2.prototype._checkFiles = function(resources) { 1232 | var resource; 1233 | var result = { 1234 | mtl: null, 1235 | obj: null 1236 | }; 1237 | for (var index in resources) { 1238 | 1239 | resource = resources[index]; 1240 | if (!Validator.isValid(resource.name)) continue; 1241 | if (Validator.isValid(resource.content)) { 1242 | 1243 | if (resource.extension === 'OBJ') { 1244 | 1245 | // fast-fail on bad type 1246 | if (!(resource.content instanceof Uint8Array)) throw 'Provided content is not of type arraybuffer! Aborting...'; 1247 | result.obj = resource; 1248 | 1249 | } else if (resource.extension === 'MTL' && Validator.isValid(resource.name)) { 1250 | 1251 | if (!(typeof(resource.content) === 'string' || resource.content instanceof String)) throw 'Provided content is not of type String! Aborting...'; 1252 | result.mtl = resource; 1253 | 1254 | } else if (resource.extension === "ZIP") { 1255 | // ignore 1256 | 1257 | } else { 1258 | 1259 | throw 'Unidentified resource "' + resource.name + '": ' + resource.url; 1260 | 1261 | } 1262 | 1263 | } else { 1264 | 1265 | // fast-fail on bad type 1266 | if (!(typeof(resource.name) === 'string' || resource.name instanceof String)) throw 'Provided file is not properly defined! Aborting...'; 1267 | if (resource.extension === 'OBJ') { 1268 | 1269 | result.obj = resource; 1270 | 1271 | } else if (resource.extension === 'MTL') { 1272 | 1273 | result.mtl = resource; 1274 | 1275 | } else if (resource.extension === "ZIP") { 1276 | // ignore 1277 | 1278 | } else { 1279 | 1280 | throw 'Unidentified resource "' + resource.name + '": ' + resource.url; 1281 | 1282 | } 1283 | } 1284 | } 1285 | 1286 | return result; 1287 | }; 1288 | 1289 | /** 1290 | * Utility method for loading an mtl file according resource description. 1291 | * @memberOf THREE.OBJLoader2 1292 | * 1293 | * @param {string} url URL to the file 1294 | * @param {Object} content The file content as arraybuffer or text 1295 | * @param {function} callbackOnLoad 1296 | * @param {string} [crossOrigin] CORS value 1297 | */ 1298 | OBJLoader2.prototype.loadMtl = function(url, content, callbackOnLoad, crossOrigin) { 1299 | var resource = new THREE.LoaderSupport.ResourceDescriptor(url, 'MTL'); 1300 | resource.setContent(content); 1301 | this._loadMtl(resource, callbackOnLoad, crossOrigin); 1302 | }; 1303 | 1304 | /** 1305 | * Utility method for loading an mtl file according resource description. 1306 | * @memberOf THREE.OBJLoader2 1307 | * 1308 | * @param {THREE.LoaderSupport.ResourceDescriptor} resource 1309 | * @param {function} callbackOnLoad 1310 | * @param {string} [crossOrigin] CORS value 1311 | */ 1312 | OBJLoader2.prototype._loadMtl = function(resource, callbackOnLoad, crossOrigin) { 1313 | if (THREE.MTLLoader === undefined) console.error('"THREE.MTLLoader" is not available. "THREE.OBJLoader2" requires it for loading MTL files.'); 1314 | if (Validator.isValid(resource)) this.logger.logTimeStart('Loading MTL: ' + resource.name); 1315 | 1316 | var materials = []; 1317 | var scope = this; 1318 | var processMaterials = function(materialCreator) { 1319 | var materialCreatorMaterials = []; 1320 | if (Validator.isValid(materialCreator)) { 1321 | 1322 | materialCreator.preload(); 1323 | materialCreatorMaterials = materialCreator.materials; 1324 | for (var materialName in materialCreatorMaterials) { 1325 | 1326 | if (materialCreatorMaterials.hasOwnProperty(materialName)) { 1327 | 1328 | materials[materialName] = materialCreatorMaterials[materialName]; 1329 | 1330 | } 1331 | } 1332 | } 1333 | 1334 | if (Validator.isValid(resource)) scope.logger.logTimeEnd('Loading MTL: ' + resource.name); 1335 | callbackOnLoad(materials); 1336 | }; 1337 | 1338 | var mtlLoader = new THREE.MTLLoader(); 1339 | crossOrigin = Validator.verifyInput(crossOrigin, 'anonymous'); 1340 | mtlLoader.setCrossOrigin(crossOrigin); 1341 | 1342 | // fast-fail 1343 | if (!Validator.isValid(resource) || (!Validator.isValid(resource.content) && !Validator.isValid(resource.url))) { 1344 | 1345 | processMaterials(); 1346 | 1347 | } else { 1348 | 1349 | mtlLoader.setPath(resource.path); 1350 | if (Validator.isValid(resource.content)) { 1351 | 1352 | processMaterials(Validator.isValid(resource.content) ? mtlLoader.parse(resource.content) : null); 1353 | 1354 | } else if (Validator.isValid(resource.url)) { 1355 | 1356 | var onError = function(event) { 1357 | var output = 'Error occurred while downloading "' + resource.url + '"'; 1358 | scope.logger.logError(output + ': ' + event); 1359 | throw output; 1360 | }; 1361 | 1362 | mtlLoader.load(resource.name, processMaterials, undefined, onError); 1363 | 1364 | } 1365 | } 1366 | }; 1367 | 1368 | return OBJLoader2; 1369 | })(); -------------------------------------------------------------------------------- /assets/loader/LoaderSupport.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Kai Salmen / https://kaisalmen.de 3 | * Development repository: https://github.com/kaisalmen/WWOBJLoader 4 | */ 5 | 6 | 'use strict'; 7 | 8 | if ( THREE.LoaderSupport === undefined ) { THREE.LoaderSupport = {} } 9 | 10 | /** 11 | * Validation functions. 12 | * @class 13 | */ 14 | THREE.LoaderSupport.Validator = { 15 | /** 16 | * If given input is null or undefined, false is returned otherwise true. 17 | * 18 | * @param input Can be anything 19 | * @returns {boolean} 20 | */ 21 | isValid: function( input ) { 22 | return ( input !== null && input !== undefined ); 23 | }, 24 | /** 25 | * If given input is null or undefined, the defaultValue is returned otherwise the given input. 26 | * 27 | * @param input Can be anything 28 | * @param defaultValue Can be anything 29 | * @returns {*} 30 | */ 31 | verifyInput: function( input, defaultValue ) { 32 | return ( input === null || input === undefined ) ? defaultValue : input; 33 | } 34 | }; 35 | 36 | 37 | /** 38 | * Logging wrapper for console. 39 | * @class 40 | * 41 | * @param {boolean} enabled=true Tell if logger is enabled. 42 | * @param {boolean} debug=false Toggle debug logging. 43 | */ 44 | THREE.LoaderSupport.ConsoleLogger = (function () { 45 | 46 | function ConsoleLogger( enabled, debug ) { 47 | this.enabled = enabled !== false; 48 | this.debug = debug === true; 49 | } 50 | 51 | /** 52 | * Enable or disable debug logging. 53 | * @memberOf THREE.LoaderSupport.ConsoleLogger 54 | * 55 | * @param {boolean} debug True or False 56 | */ 57 | ConsoleLogger.prototype.setDebug = function ( debug ) { 58 | this.debug = debug === true; 59 | }; 60 | 61 | /** 62 | * Returns if is enabled and debug. 63 | * @memberOf THREE.LoaderSupport.ConsoleLogger 64 | * 65 | * @returns {boolean} 66 | */ 67 | ConsoleLogger.prototype.isDebug = function () { 68 | return this.isEnabled() && this.debug; 69 | }; 70 | 71 | /** 72 | * Enable or disable info, debug and time logging. 73 | * @memberOf THREE.LoaderSupport.ConsoleLogger 74 | * 75 | * @param {boolean} enabled True or False 76 | */ 77 | ConsoleLogger.prototype.setEnabled = function ( enabled ) { 78 | this.enabled = enabled === true; 79 | }; 80 | 81 | /** 82 | * Returns if is enabled. 83 | * @memberOf THREE.LoaderSupport.ConsoleLogger 84 | * 85 | * @returns {boolean} 86 | */ 87 | ConsoleLogger.prototype.isEnabled = function () { 88 | return this.enabled; 89 | }; 90 | 91 | /** 92 | * Log a debug message if enabled and debug is set. 93 | * @memberOf THREE.LoaderSupport.ConsoleLogger 94 | * 95 | * @param {string} message Message to log 96 | */ 97 | ConsoleLogger.prototype.logDebug = function ( message ) { 98 | if ( this.enabled && this.debug ) console.log( message ); 99 | }; 100 | 101 | /** 102 | * Log an info message if enabled. 103 | * @memberOf THREE.LoaderSupport.ConsoleLogger 104 | * 105 | * @param {string} message Message to log 106 | */ 107 | ConsoleLogger.prototype.logInfo = function ( message ) { 108 | if ( this.enabled ) console.log( message ); 109 | }; 110 | 111 | /** 112 | * Log a warn message (always). 113 | * @memberOf THREE.LoaderSupport.ConsoleLogger 114 | * 115 | * @param {string} message Message to log 116 | */ 117 | ConsoleLogger.prototype.logWarn = function ( message ) { 118 | console.warn( message ); 119 | }; 120 | 121 | /** 122 | * Log an error message (always). 123 | * @memberOf THREE.LoaderSupport.ConsoleLogger 124 | * 125 | * @param {string} message Message to log 126 | */ 127 | ConsoleLogger.prototype.logError = function ( message ) { 128 | console.error( message ); 129 | }; 130 | 131 | /** 132 | * Start time measurement with provided id. 133 | * @memberOf THREE.LoaderSupport.ConsoleLogger 134 | * 135 | * @param {string} id Time identification 136 | */ 137 | ConsoleLogger.prototype.logTimeStart = function ( id ) { 138 | // if ( this.enabled ) console.time( id ); 139 | }; 140 | 141 | /** 142 | * Stop time measurement started with provided id. 143 | * @memberOf THREE.LoaderSupport.ConsoleLogger 144 | * 145 | * @param {string} id Time identification 146 | */ 147 | ConsoleLogger.prototype.logTimeEnd = function ( id ) { 148 | // if ( this.enabled ) console.timeEnd( id ); 149 | }; 150 | 151 | return ConsoleLogger; 152 | })(); 153 | 154 | /** 155 | * Callbacks utilized by loaders and builder. 156 | * @class 157 | */ 158 | THREE.LoaderSupport.Callbacks = (function () { 159 | 160 | var Validator = THREE.LoaderSupport.Validator; 161 | 162 | function Callbacks() { 163 | this.onProgress = null; 164 | this.onMeshAlter = null; 165 | this.onLoad = null; 166 | this.onLoadMaterials = null; 167 | } 168 | 169 | /** 170 | * Register callback function that is invoked by internal function "announceProgress" to print feedback. 171 | * @memberOf THREE.LoaderSupport.Callbacks 172 | * 173 | * @param {callback} callbackOnProgress Callback function for described functionality 174 | */ 175 | Callbacks.prototype.setCallbackOnProgress = function ( callbackOnProgress ) { 176 | this.onProgress = Validator.verifyInput( callbackOnProgress, this.onProgress ); 177 | }; 178 | 179 | /** 180 | * Register callback function that is called every time a mesh was loaded. 181 | * Use {@link THREE.LoaderSupport.LoadedMeshUserOverride} for alteration instructions (geometry, material or disregard mesh). 182 | * @memberOf THREE.LoaderSupport.Callbacks 183 | * 184 | * @param {callback} callbackOnMeshAlter Callback function for described functionality 185 | */ 186 | Callbacks.prototype.setCallbackOnMeshAlter = function ( callbackOnMeshAlter ) { 187 | this.onMeshAlter = Validator.verifyInput( callbackOnMeshAlter, this.onMeshAlter ); 188 | }; 189 | 190 | /** 191 | * Register callback function that is called once loading of the complete OBJ file is completed. 192 | * @memberOf THREE.LoaderSupport.Callbacks 193 | * 194 | * @param {callback} callbackOnLoad Callback function for described functionality 195 | */ 196 | Callbacks.prototype.setCallbackOnLoad = function ( callbackOnLoad ) { 197 | this.onLoad = Validator.verifyInput( callbackOnLoad, this.onLoad ); 198 | }; 199 | 200 | /** 201 | * Register callback function that is called when materials have been loaded. 202 | * @memberOf THREE.LoaderSupport.Callbacks 203 | * 204 | * @param {callback} callbackOnLoadMaterials Callback function for described functionality 205 | */ 206 | Callbacks.prototype.setCallbackOnLoadMaterials = function ( callbackOnLoadMaterials ) { 207 | this.onLoadMaterials = Validator.verifyInput( callbackOnLoadMaterials, this.onLoadMaterials ); 208 | }; 209 | 210 | return Callbacks; 211 | })(); 212 | 213 | 214 | /** 215 | * Object to return by callback onMeshAlter. Used to disregard a certain mesh or to return one to many meshes. 216 | * @class 217 | * 218 | * @param {boolean} disregardMesh=false Tell implementation to completely disregard this mesh 219 | * @param {boolean} disregardMesh=false Tell implementation that mesh(es) have been altered or added 220 | */ 221 | THREE.LoaderSupport.LoadedMeshUserOverride = (function () { 222 | 223 | function LoadedMeshUserOverride( disregardMesh, alteredMesh ) { 224 | this.disregardMesh = disregardMesh === true; 225 | this.alteredMesh = alteredMesh === true; 226 | this.meshes = []; 227 | } 228 | 229 | /** 230 | * Add a mesh created within callback. 231 | * 232 | * @memberOf THREE.OBJLoader2.LoadedMeshUserOverride 233 | * 234 | * @param {THREE.Mesh} mesh 235 | */ 236 | LoadedMeshUserOverride.prototype.addMesh = function ( mesh ) { 237 | this.meshes.push( mesh ); 238 | this.alteredMesh = true; 239 | }; 240 | 241 | /** 242 | * Answers if mesh shall be disregarded completely. 243 | * 244 | * @returns {boolean} 245 | */ 246 | LoadedMeshUserOverride.prototype.isDisregardMesh = function () { 247 | return this.disregardMesh; 248 | }; 249 | 250 | /** 251 | * Answers if new mesh(es) were created. 252 | * 253 | * @returns {boolean} 254 | */ 255 | LoadedMeshUserOverride.prototype.providesAlteredMeshes = function () { 256 | return this.alteredMesh; 257 | }; 258 | 259 | return LoadedMeshUserOverride; 260 | })(); 261 | 262 | 263 | /** 264 | * A resource description used by {@link THREE.LoaderSupport.PrepData} and others. 265 | * @class 266 | * 267 | * @param {string} url URL to the file 268 | * @param {string} extension The file extension (type) 269 | */ 270 | THREE.LoaderSupport.ResourceDescriptor = (function () { 271 | 272 | var Validator = THREE.LoaderSupport.Validator; 273 | 274 | function ResourceDescriptor( url, extension ) { 275 | var urlParts = url.split( '/' ); 276 | 277 | if ( urlParts.length < 2 ) { 278 | 279 | this.path = null; 280 | this.name = url; 281 | this.url = url; 282 | 283 | } else { 284 | 285 | this.path = Validator.verifyInput( urlParts.slice( 0, urlParts.length - 1).join( '/' ) + '/', null ); 286 | this.name = Validator.verifyInput( urlParts[ urlParts.length - 1 ], null ); 287 | this.url = url; 288 | 289 | } 290 | this.extension = Validator.verifyInput( extension, "default" ); 291 | this.extension = this.extension.trim(); 292 | this.content = null; 293 | } 294 | 295 | /** 296 | * Set the content of this resource (String) 297 | * @memberOf THREE.LoaderSupport.ResourceDescriptor 298 | * 299 | * @param {Object} content The file content as arraybuffer or text 300 | */ 301 | ResourceDescriptor.prototype.setContent = function ( content ) { 302 | this.content = Validator.verifyInput( content, null ); 303 | }; 304 | 305 | return ResourceDescriptor; 306 | })(); 307 | 308 | 309 | /** 310 | * Configuration instructions to be used by run method. 311 | * @class 312 | */ 313 | THREE.LoaderSupport.PrepData = (function () { 314 | 315 | var Validator = THREE.LoaderSupport.Validator; 316 | 317 | function PrepData( modelName ) { 318 | this.modelName = Validator.verifyInput( modelName, '' ); 319 | this.resources = []; 320 | this.streamMeshesTo = null; 321 | this.materialPerSmoothingGroup = false; 322 | this.useIndices = false; 323 | this.disregardNormals = false; 324 | this.callbacks = new THREE.LoaderSupport.Callbacks(); 325 | this.crossOrigin; 326 | this.useAsync = false; 327 | } 328 | 329 | /** 330 | * Set the node where the loaded objects will be attached directly. 331 | * @memberOf THREE.LoaderSupport.PrepData 332 | * 333 | * @param {THREE.Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to 334 | */ 335 | PrepData.prototype.setStreamMeshesTo = function ( streamMeshesTo ) { 336 | this.streamMeshesTo = Validator.verifyInput( streamMeshesTo, null ); 337 | }; 338 | 339 | /** 340 | * Tells whether a material shall be created per smoothing group. 341 | * @memberOf THREE.LoaderSupport.PrepData 342 | * 343 | * @param {boolean} materialPerSmoothingGroup=false 344 | */ 345 | PrepData.prototype.setMaterialPerSmoothingGroup = function ( materialPerSmoothingGroup ) { 346 | this.materialPerSmoothingGroup = materialPerSmoothingGroup === true; 347 | }; 348 | 349 | /** 350 | * Tells whether indices should be used 351 | * @memberOf THREE.LoaderSupport.PrepData 352 | * 353 | * @param {boolean} useIndices=false 354 | */ 355 | PrepData.prototype.setUseIndices = function ( useIndices ) { 356 | this.useIndices = useIndices === true; 357 | }; 358 | 359 | /** 360 | * Tells whether normals should be completely disregarded and regenerated. 361 | * @memberOf THREE.LoaderSupport.PrepData 362 | * 363 | * @param {boolean} disregardNormals=false 364 | */ 365 | PrepData.prototype.setDisregardNormals = function ( disregardNormals ) { 366 | this.disregardNormals = disregardNormals === true; 367 | }; 368 | 369 | /** 370 | * Returns all callbacks as {@link THREE.LoaderSupport.Callbacks} 371 | * @memberOf THREE.LoaderSupport.PrepData 372 | * 373 | * @returns {THREE.LoaderSupport.Callbacks} 374 | */ 375 | PrepData.prototype.getCallbacks = function () { 376 | return this.callbacks; 377 | }; 378 | 379 | /** 380 | * Sets the CORS string to be used. 381 | * @memberOf THREE.LoaderSupport.PrepData 382 | * 383 | * @param {string} crossOrigin CORS value 384 | */ 385 | PrepData.prototype.setCrossOrigin = function ( crossOrigin ) { 386 | this.crossOrigin = crossOrigin; 387 | }; 388 | 389 | /** 390 | * Add a resource description. 391 | * @memberOf THREE.LoaderSupport.PrepData 392 | * 393 | * @param {THREE.LoaderSupport.ResourceDescriptor} 394 | */ 395 | PrepData.prototype.addResource = function ( resource ) { 396 | this.resources.push( resource ); 397 | }; 398 | 399 | /** 400 | * If true uses async loading with worker, if false loads data synchronously. 401 | * @memberOf THREE.LoaderSupport.PrepData 402 | * 403 | * @param {boolean} useAsync 404 | */ 405 | PrepData.prototype.setUseAsync = function ( useAsync ) { 406 | this.useAsync = useAsync === true; 407 | }; 408 | 409 | /** 410 | * Clones this object and returns it afterwards. 411 | * @memberOf THREE.LoaderSupport.PrepData 412 | * 413 | * @returns {@link THREE.LoaderSupport.PrepData} 414 | */ 415 | PrepData.prototype.clone = function () { 416 | var clone = new THREE.LoaderSupport.PrepData( this.modelName ); 417 | clone.resources = this.resources; 418 | clone.streamMeshesTo = this.streamMeshesTo; 419 | clone.materialPerSmoothingGroup = this.materialPerSmoothingGroup; 420 | clone.useIndices = this.useIndices; 421 | clone.disregardNormals = this.disregardNormals; 422 | clone.callbacks = this.callbacks; 423 | clone.crossOrigin = this.crossOrigin; 424 | clone.useAsync = this.useAsync; 425 | return clone; 426 | }; 427 | 428 | return PrepData; 429 | })(); 430 | 431 | /** 432 | * Builds one or many THREE.Mesh from one raw set of Arraybuffers, materialGroup descriptions and further parameters. 433 | * Supports vertex, vertexColor, normal, uv and index buffers. 434 | * @class 435 | */ 436 | THREE.LoaderSupport.Builder = (function () { 437 | 438 | var LOADER_BUILDER_VERSION = '1.1.1'; 439 | 440 | var Validator = THREE.LoaderSupport.Validator; 441 | var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; 442 | 443 | function Builder( logger ) { 444 | this.logger = Validator.verifyInput( logger, new ConsoleLogger() ); 445 | this.logger.logInfo( 'Using THREE.LoaderSupport.Builder version: ' + LOADER_BUILDER_VERSION ); 446 | this.callbacks = new THREE.LoaderSupport.Callbacks(); 447 | this.materials = []; 448 | } 449 | 450 | /** 451 | * Set materials loaded by any supplier of an Array of {@link THREE.Material}. 452 | * @memberOf THREE.LoaderSupport.Builder 453 | * 454 | * @param {THREE.Material[]} materials Array of {@link THREE.Material} 455 | */ 456 | Builder.prototype.setMaterials = function ( materials ) { 457 | var payload = { 458 | cmd: 'materialData', 459 | materials: { 460 | materialCloneInstructions: null, 461 | serializedMaterials: null, 462 | runtimeMaterials: Validator.isValid( this.callbacks.onLoadMaterials ) ? this.callbacks.onLoadMaterials( materials ) : materials 463 | } 464 | }; 465 | this.updateMaterials( payload ); 466 | }; 467 | 468 | Builder.prototype._setCallbacks = function ( callbacks ) { 469 | if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress ); 470 | if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter ); 471 | if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad ); 472 | if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials ); 473 | }; 474 | 475 | /** 476 | * Delegates processing of the payload (mesh building or material update) to the corresponding functions (BW-compatibility). 477 | * @memberOf THREE.LoaderSupport.Builder 478 | * 479 | * @param {Object} payload Raw Mesh or Material descriptions. 480 | * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} or null in case of material update 481 | */ 482 | Builder.prototype.processPayload = function ( payload ) { 483 | if ( payload.cmd === 'meshData' ) { 484 | 485 | return this.buildMeshes( payload ); 486 | 487 | } else if ( payload.cmd === 'materialData' ) { 488 | 489 | this.updateMaterials( payload ); 490 | return null; 491 | 492 | } 493 | }; 494 | 495 | /** 496 | * Builds one or multiple meshes from the data described in the payload (buffers, params, material info). 497 | * @memberOf THREE.LoaderSupport.Builder 498 | * 499 | * @param {Object} meshPayload Raw mesh description (buffers, params, materials) used to build one to many meshes. 500 | * @returns {THREE.Mesh[]} mesh Array of {@link THREE.Mesh} 501 | */ 502 | Builder.prototype.buildMeshes = function ( meshPayload ) { 503 | var meshName = meshPayload.params.meshName; 504 | 505 | var bufferGeometry = new THREE.BufferGeometry(); 506 | bufferGeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.vertices ), 3 ) ); 507 | if ( Validator.isValid( meshPayload.buffers.indices ) ) { 508 | 509 | bufferGeometry.setIndex( new THREE.BufferAttribute( new Uint32Array( meshPayload.buffers.indices ), 1 )); 510 | 511 | } 512 | var haveVertexColors = Validator.isValid( meshPayload.buffers.colors ); 513 | if ( haveVertexColors ) { 514 | 515 | bufferGeometry.addAttribute( 'color', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.colors ), 3 ) ); 516 | 517 | } 518 | if ( Validator.isValid( meshPayload.buffers.normals ) ) { 519 | 520 | bufferGeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.normals ), 3 ) ); 521 | 522 | } else { 523 | 524 | bufferGeometry.computeVertexNormals(); 525 | 526 | } 527 | if ( Validator.isValid( meshPayload.buffers.uvs ) ) { 528 | 529 | bufferGeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( meshPayload.buffers.uvs ), 2 ) ); 530 | 531 | } 532 | 533 | var material, materialName, key; 534 | var materialNames = meshPayload.materials.materialNames; 535 | var createMultiMaterial = meshPayload.materials.multiMaterial; 536 | var multiMaterials = []; 537 | for ( key in materialNames ) { 538 | 539 | materialName = materialNames[ key ]; 540 | material = this.materials[ materialName ]; 541 | if ( createMultiMaterial ) multiMaterials.push( material ); 542 | 543 | } 544 | if ( createMultiMaterial ) { 545 | 546 | material = multiMaterials; 547 | var materialGroups = meshPayload.materials.materialGroups; 548 | var materialGroup; 549 | for ( key in materialGroups ) { 550 | 551 | materialGroup = materialGroups[ key ]; 552 | bufferGeometry.addGroup( materialGroup.start, materialGroup.count, materialGroup.index ); 553 | 554 | } 555 | 556 | } 557 | 558 | var meshes = []; 559 | var mesh; 560 | var callbackOnMeshAlter = this.callbacks.onMeshAlter; 561 | var callbackOnMeshAlterResult; 562 | var useOrgMesh = true; 563 | if ( Validator.isValid( callbackOnMeshAlter ) ) { 564 | 565 | callbackOnMeshAlterResult = callbackOnMeshAlter( 566 | { 567 | detail: { 568 | meshName: meshName, 569 | bufferGeometry: bufferGeometry, 570 | material: material 571 | } 572 | } 573 | ); 574 | if ( Validator.isValid( callbackOnMeshAlterResult ) ) { 575 | 576 | if ( ! callbackOnMeshAlterResult.isDisregardMesh() && callbackOnMeshAlterResult.providesAlteredMeshes() ) { 577 | 578 | for ( var i in callbackOnMeshAlterResult.meshes ) { 579 | 580 | meshes.push( callbackOnMeshAlterResult.meshes[ i ] ); 581 | 582 | } 583 | 584 | } 585 | useOrgMesh = false; 586 | 587 | } 588 | 589 | } 590 | if ( useOrgMesh ) { 591 | 592 | mesh = new THREE.Mesh( bufferGeometry, material ); 593 | mesh.name = meshName; 594 | meshes.push( mesh ); 595 | 596 | } 597 | 598 | var progressMessage; 599 | if ( Validator.isValid( meshes ) && meshes.length > 0 ) { 600 | 601 | var meshNames = []; 602 | for ( var i in meshes ) { 603 | 604 | mesh = meshes[ i ]; 605 | meshNames[ i ] = mesh.name; 606 | 607 | } 608 | progressMessage = 'Adding mesh(es) (' + meshNames.length + ': ' + meshNames + ') from input mesh: ' + meshName; 609 | progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)'; 610 | 611 | } else { 612 | 613 | progressMessage = 'Not adding mesh: ' + meshName; 614 | progressMessage += ' (' + ( meshPayload.progress.numericalValue * 100 ).toFixed( 2 ) + '%)'; 615 | 616 | } 617 | var callbackOnProgress = this.callbacks.onProgress; 618 | if ( Validator.isValid( callbackOnProgress ) ) { 619 | 620 | var event = new CustomEvent( 'BuilderEvent', { 621 | detail: { 622 | type: 'progress', 623 | modelName: meshPayload.params.meshName, 624 | text: progressMessage, 625 | numericalValue: meshPayload.progress.numericalValue 626 | } 627 | } ); 628 | callbackOnProgress( event ); 629 | 630 | } 631 | 632 | return meshes; 633 | }; 634 | 635 | /** 636 | * Updates the materials with contained material objects (sync) or from alteration instructions (async). 637 | * @memberOf THREE.LoaderSupport.Builder 638 | * 639 | * @param {Object} materialPayload Material update instructions 640 | */ 641 | Builder.prototype.updateMaterials = function ( materialPayload ) { 642 | var material, materialName; 643 | var materialCloneInstructions = materialPayload.materials.materialCloneInstructions; 644 | if ( Validator.isValid( materialCloneInstructions ) ) { 645 | 646 | var materialNameOrg = materialCloneInstructions.materialNameOrg; 647 | var materialOrg = this.materials[ materialNameOrg ]; 648 | material = materialOrg.clone(); 649 | 650 | materialName = materialCloneInstructions.materialName; 651 | material.name = materialName; 652 | 653 | var materialProperties = materialCloneInstructions.materialProperties; 654 | for ( var key in materialProperties ) { 655 | 656 | if ( material.hasOwnProperty( key ) && materialProperties.hasOwnProperty( key ) ) material[ key ] = materialProperties[ key ]; 657 | 658 | } 659 | this.materials[ materialName ] = material; 660 | 661 | } 662 | 663 | var materials = materialPayload.materials.serializedMaterials; 664 | if ( Validator.isValid( materials ) && Object.keys( materials ).length > 0 ) { 665 | 666 | var loader = new THREE.MaterialLoader(); 667 | var materialJson; 668 | for ( materialName in materials ) { 669 | 670 | materialJson = materials[ materialName ]; 671 | if ( Validator.isValid( materialJson ) ) { 672 | 673 | material = loader.parse( materialJson ); 674 | this.logger.logInfo( 'De-serialized material with name "' + materialName + '" will be added.' ); 675 | this.materials[ materialName ] = material; 676 | } 677 | 678 | } 679 | 680 | } 681 | 682 | materials = materialPayload.materials.runtimeMaterials; 683 | if ( Validator.isValid( materials ) && Object.keys( materials ).length > 0 ) { 684 | 685 | for ( materialName in materials ) { 686 | 687 | material = materials[ materialName ]; 688 | this.logger.logInfo( 'Material with name "' + materialName + '" will be added.' ); 689 | this.materials[ materialName ] = material; 690 | 691 | } 692 | 693 | } 694 | }; 695 | 696 | /** 697 | * Returns the mapping object of material name and corresponding jsonified material. 698 | * 699 | * @returns {Object} Map of Materials in JSON representation 700 | */ 701 | Builder.prototype.getMaterialsJSON = function () { 702 | var materialsJSON = {}; 703 | var material; 704 | for ( var materialName in this.materials ) { 705 | 706 | material = this.materials[ materialName ]; 707 | materialsJSON[ materialName ] = material.toJSON(); 708 | } 709 | 710 | return materialsJSON; 711 | }; 712 | 713 | /** 714 | * Returns the mapping object of material name and corresponding material. 715 | * 716 | * @returns {Object} Map of {@link THREE.Material} 717 | */ 718 | Builder.prototype.getMaterials = function () { 719 | return this.materials; 720 | }; 721 | 722 | return Builder; 723 | })(); 724 | 725 | /** 726 | * Base class to be used by loaders. 727 | * @class 728 | * 729 | * @param {THREE.DefaultLoadingManager} [manager] The loadingManager for the loader to use. Default is {@link THREE.DefaultLoadingManager} 730 | * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used 731 | */ 732 | THREE.LoaderSupport.LoaderBase = (function () { 733 | 734 | var Validator = THREE.LoaderSupport.Validator; 735 | var ConsoleLogger = THREE.LoaderSupport.ConsoleLogger; 736 | 737 | function LoaderBase( manager, logger ) { 738 | this.manager = Validator.verifyInput( manager, THREE.DefaultLoadingManager ); 739 | this.logger = Validator.verifyInput( logger, new ConsoleLogger() ); 740 | 741 | this.modelName = ''; 742 | this.instanceNo = 0; 743 | this.path = ''; 744 | this.useIndices = false; 745 | this.disregardNormals = false; 746 | 747 | this.loaderRootNode = new THREE.Group(); 748 | this.builder = new THREE.LoaderSupport.Builder( this.logger ); 749 | this._createDefaultMaterials(); 750 | this.callbacks = new THREE.LoaderSupport.Callbacks(); 751 | }; 752 | 753 | LoaderBase.prototype._createDefaultMaterials = function () { 754 | var defaultMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } ); 755 | defaultMaterial.name = 'defaultMaterial'; 756 | 757 | var vertexColorMaterial = new THREE.MeshStandardMaterial( { color: 0xDCF1FF } ); 758 | vertexColorMaterial.name = 'vertexColorMaterial'; 759 | vertexColorMaterial.vertexColors = THREE.VertexColors; 760 | 761 | var runtimeMaterials = {}; 762 | runtimeMaterials[ defaultMaterial.name ] = defaultMaterial; 763 | runtimeMaterials[ vertexColorMaterial.name ] = vertexColorMaterial; 764 | 765 | this.builder.updateMaterials( 766 | { 767 | cmd: 'materialData', 768 | materials: { 769 | materialCloneInstructions: null, 770 | serializedMaterials: null, 771 | runtimeMaterials: runtimeMaterials 772 | } 773 | } 774 | ); 775 | }; 776 | 777 | LoaderBase.prototype._applyPrepData = function ( prepData ) { 778 | if ( Validator.isValid( prepData ) ) { 779 | 780 | this.setModelName( prepData.modelName ); 781 | this.setStreamMeshesTo( prepData.streamMeshesTo ); 782 | this.builder.setMaterials( prepData.materials ); 783 | this.setUseIndices( prepData.useIndices ); 784 | this.setDisregardNormals( prepData.disregardNormals ); 785 | 786 | this._setCallbacks( prepData.getCallbacks() ); 787 | } 788 | }; 789 | 790 | LoaderBase.prototype._setCallbacks = function ( callbacks ) { 791 | if ( Validator.isValid( callbacks.onProgress ) ) this.callbacks.setCallbackOnProgress( callbacks.onProgress ); 792 | if ( Validator.isValid( callbacks.onMeshAlter ) ) this.callbacks.setCallbackOnMeshAlter( callbacks.onMeshAlter ); 793 | if ( Validator.isValid( callbacks.onLoad ) ) this.callbacks.setCallbackOnLoad( callbacks.onLoad ); 794 | if ( Validator.isValid( callbacks.onLoadMaterials ) ) this.callbacks.setCallbackOnLoadMaterials( callbacks.onLoadMaterials ); 795 | 796 | this.builder._setCallbacks( this.callbacks ); 797 | }; 798 | 799 | /** 800 | * Provides access to console logging wrapper. 801 | * 802 | * @returns {THREE.LoaderSupport.ConsoleLogger} 803 | */ 804 | LoaderBase.prototype.getLogger = function () { 805 | return this.logger; 806 | }; 807 | 808 | /** 809 | * Set the name of the model. 810 | * @memberOf THREE.LoaderSupport.LoaderBase 811 | * 812 | * @param {string} modelName 813 | */ 814 | LoaderBase.prototype.setModelName = function ( modelName ) { 815 | this.modelName = Validator.verifyInput( modelName, this.modelName ); 816 | }; 817 | 818 | /** 819 | * The URL of the base path. 820 | * @memberOf THREE.LoaderSupport.LoaderBase 821 | * 822 | * @param {string} path URL 823 | */ 824 | LoaderBase.prototype.setPath = function ( path ) { 825 | this.path = Validator.verifyInput( path, this.path ); 826 | }; 827 | 828 | /** 829 | * Set the node where the loaded objects will be attached directly. 830 | * @memberOf THREE.LoaderSupport.LoaderBase 831 | * 832 | * @param {THREE.Object3D} streamMeshesTo Object already attached to scenegraph where new meshes will be attached to 833 | */ 834 | LoaderBase.prototype.setStreamMeshesTo = function ( streamMeshesTo ) { 835 | this.loaderRootNode = Validator.verifyInput( streamMeshesTo, this.loaderRootNode ); 836 | }; 837 | 838 | /** 839 | * Set materials loaded by MTLLoader or any other supplier of an Array of {@link THREE.Material}. 840 | * @memberOf THREE.LoaderSupport.LoaderBase 841 | * 842 | * @param {THREE.Material[]} materials Array of {@link THREE.Material} 843 | */ 844 | LoaderBase.prototype.setMaterials = function ( materials ) { 845 | this.builder.setMaterials( materials ); 846 | }; 847 | 848 | /** 849 | * Instructs loaders to create indexed {@link THREE.BufferGeometry}. 850 | * @memberOf THREE.LoaderSupport.LoaderBase 851 | * 852 | * @param {boolean} useIndices=false 853 | */ 854 | LoaderBase.prototype.setUseIndices = function ( useIndices ) { 855 | this.useIndices = useIndices === true; 856 | }; 857 | 858 | /** 859 | * Tells whether normals should be completely disregarded and regenerated. 860 | * @memberOf THREE.LoaderSupport.LoaderBase 861 | * 862 | * @param {boolean} disregardNormals=false 863 | */ 864 | LoaderBase.prototype.setDisregardNormals = function ( disregardNormals ) { 865 | this.disregardNormals = disregardNormals === true; 866 | }; 867 | 868 | /** 869 | * Announce feedback which is give to the registered callbacks. 870 | * @memberOf THREE.LoaderSupport.LoaderBase 871 | * @private 872 | * 873 | * @param {string} type The type of event 874 | * @param {string} text Textual description of the event 875 | * @param {number} numericalValue Numerical value describing the progress 876 | */ 877 | LoaderBase.prototype.onProgress = function ( type, text, numericalValue ) { 878 | var content = Validator.isValid( text ) ? text: ''; 879 | var event = { 880 | detail: { 881 | type: type, 882 | modelName: this.modelName, 883 | instanceNo: this.instanceNo, 884 | text: content, 885 | numericalValue: numericalValue 886 | } 887 | }; 888 | 889 | if ( Validator.isValid( this.callbacks.onProgress ) ) this.callbacks.onProgress( event ); 890 | 891 | this.logger.logDebug( content ); 892 | }; 893 | 894 | return LoaderBase; 895 | })(); 896 | 897 | /** 898 | * Default implementation of the WorkerRunner responsible for creation and configuration of the parser within the worker. 899 | * 900 | * @class 901 | */ 902 | THREE.LoaderSupport.WorkerRunnerRefImpl = (function () { 903 | 904 | function WorkerRunnerRefImpl() { 905 | var scope = this; 906 | var scopedRunner = function( event ) { 907 | scope.processMessage( event.data ); 908 | }; 909 | self.addEventListener( 'message', scopedRunner, false ); 910 | } 911 | 912 | /** 913 | * Applies values from parameter object via set functions or via direct assignment. 914 | * @memberOf THREE.LoaderSupport.WorkerRunnerRefImpl 915 | * 916 | * @param {Object} parser The parser instance 917 | * @param {Object} params The parameter object 918 | */ 919 | WorkerRunnerRefImpl.prototype.applyProperties = function ( parser, params ) { 920 | var property, funcName, values; 921 | for ( property in params ) { 922 | funcName = 'set' + property.substring( 0, 1 ).toLocaleUpperCase() + property.substring( 1 ); 923 | values = params[ property ]; 924 | 925 | if ( typeof parser[ funcName ] === 'function' ) { 926 | 927 | parser[ funcName ]( values ); 928 | 929 | } else if ( parser.hasOwnProperty( property ) ) { 930 | 931 | parser[ property ] = values; 932 | 933 | } 934 | } 935 | }; 936 | 937 | /** 938 | * Configures the Parser implementation according the supplied configuration object. 939 | * @memberOf THREE.LoaderSupport.WorkerRunnerRefImpl 940 | * 941 | * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. 942 | */ 943 | WorkerRunnerRefImpl.prototype.processMessage = function ( payload ) { 944 | var logEnabled = payload.logger.enabled; 945 | var logDebug = payload.logger.enabled; 946 | if ( payload.cmd === 'run' ) { 947 | 948 | var callbacks = { 949 | callbackBuilder: function ( payload ) { 950 | self.postMessage( payload ); 951 | }, 952 | callbackProgress: function ( text ) { 953 | if ( logEnabled && logDebug ) console.debug( 'WorkerRunner: progress: ' + text ); 954 | } 955 | }; 956 | 957 | // Parser is expected to be named as such 958 | var parser = new Parser(); 959 | if ( typeof parser[ 'setLogConfig' ] === 'function' ) parser.setLogConfig( logEnabled, logDebug ); 960 | this.applyProperties( parser, payload.params ); 961 | this.applyProperties( parser, payload.materials ); 962 | this.applyProperties( parser, callbacks ); 963 | parser.workerScope = self; 964 | parser.parse( payload.data.input, payload.data.options ); 965 | 966 | if ( logEnabled ) console.log( 'WorkerRunner: Run complete!' ); 967 | 968 | callbacks.callbackBuilder( { 969 | cmd: 'complete', 970 | msg: 'WorkerRunner completed run.' 971 | } ); 972 | 973 | } else { 974 | 975 | console.error( 'WorkerRunner: Received unknown command: ' + payload.cmd ); 976 | 977 | } 978 | }; 979 | 980 | return WorkerRunnerRefImpl; 981 | })(); 982 | 983 | /** 984 | * This class provides means to transform existing parser code into a web worker. It defines a simple communication protocol 985 | * which allows to configure the worker and receive raw mesh data during execution. 986 | * @class 987 | * 988 | * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used 989 | */ 990 | THREE.LoaderSupport.WorkerSupport = (function () { 991 | 992 | var WORKER_SUPPORT_VERSION = '2.0.1'; 993 | 994 | var Validator = THREE.LoaderSupport.Validator; 995 | 996 | var LoaderWorker = (function () { 997 | 998 | function LoaderWorker( logger ) { 999 | this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); 1000 | this._reset(); 1001 | } 1002 | 1003 | LoaderWorker.prototype._reset = function () { 1004 | this.worker = null; 1005 | this.runnerImplName = null; 1006 | this.callbacks = { 1007 | builder: null, 1008 | onLoad: null 1009 | }; 1010 | this.terminateRequested = false; 1011 | this.queuedMessage = null; 1012 | this.started = false; 1013 | }; 1014 | 1015 | LoaderWorker.prototype.initWorker = function ( code, runnerImplName ) { 1016 | this.runnerImplName = runnerImplName; 1017 | var blob = new Blob( [ code ], { type: 'application/javascript' } ); 1018 | this.worker = new Worker( window.URL.createObjectURL( blob ) ); 1019 | this.worker.onmessage = this._receiveWorkerMessage; 1020 | 1021 | // set referemce to this, then processing in worker scope within "_receiveWorkerMessage" can access members 1022 | this.worker.runtimeRef = this; 1023 | 1024 | // process stored queuedMessage 1025 | this._postMessage(); 1026 | }; 1027 | 1028 | /** 1029 | * Executed in worker scope 1030 | */ 1031 | LoaderWorker.prototype._receiveWorkerMessage = function ( e ) { 1032 | var payload = e.data; 1033 | switch ( payload.cmd ) { 1034 | case 'meshData': 1035 | case 'materialData': 1036 | case 'imageData': 1037 | this.runtimeRef.callbacks.builder( payload ); 1038 | break; 1039 | 1040 | case 'complete': 1041 | this.runtimeRef.queuedMessage = null; 1042 | this.started = false; 1043 | this.runtimeRef.callbacks.onLoad( payload.msg ); 1044 | 1045 | if ( this.runtimeRef.terminateRequested ) { 1046 | 1047 | this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run is complete. Terminating application on request!' ); 1048 | this.runtimeRef._terminate(); 1049 | 1050 | } 1051 | break; 1052 | 1053 | case 'error': 1054 | this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Reported error: ' + payload.msg ); 1055 | this.runtimeRef.queuedMessage = null; 1056 | this.started = false; 1057 | this.runtimeRef.callbacks.onLoad( payload.msg ); 1058 | 1059 | if ( this.runtimeRef.terminateRequested ) { 1060 | 1061 | this.runtimeRef.logger.logInfo( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Run reported error. Terminating application on request!' ); 1062 | this.runtimeRef._terminate(); 1063 | 1064 | } 1065 | break; 1066 | 1067 | default: 1068 | this.runtimeRef.logger.logError( 'WorkerSupport [' + this.runtimeRef.runnerImplName + ']: Received unknown command: ' + payload.cmd ); 1069 | break; 1070 | 1071 | } 1072 | }; 1073 | 1074 | LoaderWorker.prototype.setCallbacks = function ( builder, onLoad ) { 1075 | this.callbacks.builder = Validator.verifyInput( builder, this.callbacks.builder ); 1076 | this.callbacks.onLoad = Validator.verifyInput( onLoad, this.callbacks.onLoad ); 1077 | }; 1078 | 1079 | LoaderWorker.prototype.run = function( payload ) { 1080 | if ( Validator.isValid( this.queuedMessage ) ) { 1081 | 1082 | console.warn( 'Already processing message. Rejecting new run instruction' ); 1083 | return; 1084 | 1085 | } else { 1086 | 1087 | this.queuedMessage = payload; 1088 | this.started = true; 1089 | 1090 | } 1091 | if ( ! Validator.isValid( this.callbacks.builder ) ) throw 'Unable to run as no "builder" callback is set.'; 1092 | if ( ! Validator.isValid( this.callbacks.onLoad ) ) throw 'Unable to run as no "onLoad" callback is set.'; 1093 | if ( payload.cmd !== 'run' ) payload.cmd = 'run'; 1094 | if ( Validator.isValid( payload.logger ) ) { 1095 | 1096 | payload.logger.enabled = Validator.verifyInput( payload.logger.enabled, true ); 1097 | payload.logger.debug = Validator.verifyInput( payload.logger.debug, false ); 1098 | 1099 | } else { 1100 | 1101 | payload.logger = { 1102 | enabled: true, 1103 | debug: false 1104 | } 1105 | 1106 | } 1107 | this._postMessage(); 1108 | }; 1109 | 1110 | LoaderWorker.prototype._postMessage = function () { 1111 | if ( Validator.isValid( this.queuedMessage ) && Validator.isValid( this.worker ) ) { 1112 | 1113 | if ( this.queuedMessage.data.input instanceof ArrayBuffer ) { 1114 | 1115 | this.worker.postMessage( this.queuedMessage, [ this.queuedMessage.data.input ] ); 1116 | 1117 | } else { 1118 | 1119 | this.worker.postMessage( this.queuedMessage ); 1120 | 1121 | } 1122 | 1123 | } 1124 | }; 1125 | 1126 | LoaderWorker.prototype.setTerminateRequested = function ( terminateRequested ) { 1127 | this.terminateRequested = terminateRequested === true; 1128 | if ( this.terminateRequested && Validator.isValid( this.worker ) && ! Validator.isValid( this.queuedMessage ) && this.started ) { 1129 | 1130 | this.logger.logInfo( 'Worker is terminated immediately as it is not running!' ); 1131 | this._terminate(); 1132 | 1133 | } 1134 | }; 1135 | 1136 | LoaderWorker.prototype._terminate = function () { 1137 | this.worker.terminate(); 1138 | this._reset(); 1139 | }; 1140 | 1141 | return LoaderWorker; 1142 | 1143 | })(); 1144 | 1145 | function WorkerSupport( logger ) { 1146 | this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); 1147 | this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerSupport version: ' + WORKER_SUPPORT_VERSION ); 1148 | 1149 | // check worker support first 1150 | if ( window.Worker === undefined ) throw "This browser does not support web workers!"; 1151 | if ( window.Blob === undefined ) throw "This browser does not support Blob!"; 1152 | if ( typeof window.URL.createObjectURL !== 'function' ) throw "This browser does not support Object creation from URL!"; 1153 | 1154 | this.loaderWorker = new LoaderWorker( this.logger ); 1155 | } 1156 | 1157 | /** 1158 | * Validate the status of worker code and the derived worker. 1159 | * @memberOf THREE.LoaderSupport.WorkerSupport 1160 | * 1161 | * @param {Function} functionCodeBuilder Function that is invoked with funcBuildObject and funcBuildSingelton that allows stringification of objects and singletons. 1162 | * @param {String[]} libLocations URL of libraries that shall be added to worker code relative to libPath 1163 | * @param {String} libPath Base path used for loading libraries 1164 | * @param {THREE.LoaderSupport.WorkerRunnerRefImpl} runnerImpl The default worker parser wrapper implementation (communication and execution). An extended class could be passed here. 1165 | */ 1166 | WorkerSupport.prototype.validate = function ( functionCodeBuilder, libLocations, libPath, runnerImpl ) { 1167 | if ( Validator.isValid( this.loaderWorker.worker ) ) return; 1168 | 1169 | this.logger.logInfo( 'WorkerSupport: Building worker code...' ); 1170 | this.logger.logTimeStart( 'buildWebWorkerCode' ); 1171 | 1172 | if ( Validator.isValid( runnerImpl ) ) { 1173 | 1174 | this.logger.logInfo( 'WorkerSupport: Using "' + runnerImpl.name + '" as Runncer class for worker.' ); 1175 | 1176 | } else { 1177 | 1178 | runnerImpl = THREE.LoaderSupport.WorkerRunnerRefImpl; 1179 | this.logger.logInfo( 'WorkerSupport: Using DEFAULT "THREE.LoaderSupport.WorkerRunnerRefImpl" as Runncer class for worker.' ); 1180 | 1181 | } 1182 | 1183 | var userWorkerCode = functionCodeBuilder( buildObject, buildSingelton ); 1184 | userWorkerCode += buildSingelton( runnerImpl.name, runnerImpl.name, runnerImpl ); 1185 | userWorkerCode += 'new ' + runnerImpl.name + '();\n\n'; 1186 | 1187 | var scope = this; 1188 | if ( Validator.isValid( libLocations ) && libLocations.length > 0 ) { 1189 | 1190 | var libsContent = ''; 1191 | var loadAllLibraries = function ( path, locations ) { 1192 | if ( locations.length === 0 ) { 1193 | 1194 | scope.loaderWorker.initWorker( libsContent + userWorkerCode, scope.logger, runnerImpl.name ); 1195 | scope.logger.logTimeEnd( 'buildWebWorkerCode' ); 1196 | 1197 | } else { 1198 | 1199 | var loadedLib = function ( contentAsString ) { 1200 | libsContent += contentAsString; 1201 | loadAllLibraries( path, locations ); 1202 | }; 1203 | 1204 | var fileLoader = new THREE.FileLoader(); 1205 | fileLoader.setPath( path ); 1206 | fileLoader.setResponseType( 'text' ); 1207 | fileLoader.load( locations[ 0 ], loadedLib ); 1208 | locations.shift(); 1209 | 1210 | } 1211 | }; 1212 | loadAllLibraries( libPath, libLocations ); 1213 | 1214 | } else { 1215 | 1216 | this.loaderWorker.initWorker( userWorkerCode, this.logger, runnerImpl.name ); 1217 | this.logger.logTimeEnd( 'buildWebWorkerCode' ); 1218 | 1219 | } 1220 | }; 1221 | 1222 | /** 1223 | * Specify functions that should be build when new raw mesh data becomes available and when the parser is finished. 1224 | * @memberOf THREE.LoaderSupport.WorkerSupport 1225 | * 1226 | * @param {Function} builder The builder function. Default is {@link THREE.LoaderSupport.Builder}. 1227 | * @param {Function} onLoad The function that is called when parsing is complete. 1228 | */ 1229 | WorkerSupport.prototype.setCallbacks = function ( builder, onLoad ) { 1230 | this.loaderWorker.setCallbacks( builder, onLoad ); 1231 | }; 1232 | 1233 | /** 1234 | * Runs the parser with the provided configuration. 1235 | * @memberOf THREE.LoaderSupport.WorkerSupport 1236 | * 1237 | * @param {Object} payload Raw mesh description (buffers, params, materials) used to build one to many meshes. 1238 | */ 1239 | WorkerSupport.prototype.run = function ( payload ) { 1240 | this.loaderWorker.run( payload ); 1241 | }; 1242 | 1243 | /** 1244 | * Request termination of worker once parser is finished. 1245 | * @memberOf THREE.LoaderSupport.WorkerSupport 1246 | * 1247 | * @param {boolean} terminateRequested True or false. 1248 | */ 1249 | WorkerSupport.prototype.setTerminateRequested = function ( terminateRequested ) { 1250 | this.loaderWorker.setTerminateRequested( terminateRequested ); 1251 | }; 1252 | 1253 | var buildObject = function ( fullName, object ) { 1254 | var objectString = fullName + ' = {\n'; 1255 | var part; 1256 | for ( var name in object ) { 1257 | 1258 | part = object[ name ]; 1259 | if ( typeof( part ) === 'string' || part instanceof String ) { 1260 | 1261 | part = part.replace( '\n', '\\n' ); 1262 | part = part.replace( '\r', '\\r' ); 1263 | objectString += '\t' + name + ': "' + part + '",\n'; 1264 | 1265 | } else if ( part instanceof Array ) { 1266 | 1267 | objectString += '\t' + name + ': [' + part + '],\n'; 1268 | 1269 | } else if ( Number.isInteger( part ) ) { 1270 | 1271 | objectString += '\t' + name + ': ' + part + ',\n'; 1272 | 1273 | } else if ( typeof part === 'function' ) { 1274 | 1275 | objectString += '\t' + name + ': ' + part + ',\n'; 1276 | 1277 | } 1278 | 1279 | } 1280 | objectString += '}\n\n'; 1281 | 1282 | return objectString; 1283 | }; 1284 | 1285 | var buildSingelton = function ( fullName, internalName, object ) { 1286 | var objectString = fullName + ' = (function () {\n\n'; 1287 | objectString += '\t' + object.prototype.constructor.toString() + '\n\n'; 1288 | objectString = objectString.replace( object.name, internalName ); 1289 | 1290 | var funcString; 1291 | var objectPart; 1292 | for ( var name in object.prototype ) { 1293 | 1294 | objectPart = object.prototype[ name ]; 1295 | if ( typeof objectPart === 'function' ) { 1296 | 1297 | funcString = objectPart.toString(); 1298 | objectString += '\t' + internalName + '.prototype.' + name + ' = ' + funcString + ';\n\n'; 1299 | 1300 | } 1301 | 1302 | } 1303 | objectString += '\treturn ' + internalName + ';\n'; 1304 | objectString += '})();\n\n'; 1305 | 1306 | return objectString; 1307 | }; 1308 | 1309 | return WorkerSupport; 1310 | 1311 | })(); 1312 | 1313 | /** 1314 | * Orchestrate loading of multiple OBJ files/data from an instruction queue with a configurable amount of workers (1-16). 1315 | * Workflow: 1316 | * prepareWorkers 1317 | * enqueueForRun 1318 | * processQueue 1319 | * tearDown (to force stop) 1320 | * 1321 | * @class 1322 | * 1323 | * @param {string} classDef Class definition to be used for construction 1324 | * @param {THREE.LoaderSupport.ConsoleLogger} logger logger to be used 1325 | */ 1326 | THREE.LoaderSupport.WorkerDirector = (function () { 1327 | 1328 | var LOADER_WORKER_DIRECTOR_VERSION = '2.1.0'; 1329 | 1330 | var Validator = THREE.LoaderSupport.Validator; 1331 | 1332 | var MAX_WEB_WORKER = 16; 1333 | var MAX_QUEUE_SIZE = 8192; 1334 | 1335 | function WorkerDirector( classDef, logger ) { 1336 | this.logger = Validator.verifyInput( logger, new THREE.LoaderSupport.ConsoleLogger() ); 1337 | this.logger.logInfo( 'Using THREE.LoaderSupport.WorkerDirector version: ' + LOADER_WORKER_DIRECTOR_VERSION ); 1338 | 1339 | this.maxQueueSize = MAX_QUEUE_SIZE ; 1340 | this.maxWebWorkers = MAX_WEB_WORKER; 1341 | this.crossOrigin = null; 1342 | 1343 | if ( ! Validator.isValid( classDef ) ) throw 'Provided invalid classDef: ' + classDef; 1344 | 1345 | this.workerDescription = { 1346 | classDef: classDef, 1347 | globalCallbacks: {}, 1348 | workerSupports: {} 1349 | }; 1350 | this.objectsCompleted = 0; 1351 | this.instructionQueue = []; 1352 | this.instructionQueuePointer = 0; 1353 | 1354 | this.callbackOnFinishedProcessing = null; 1355 | } 1356 | 1357 | /** 1358 | * Returns the maximum length of the instruction queue. 1359 | * @memberOf THREE.LoaderSupport.WorkerDirector 1360 | * 1361 | * @returns {number} 1362 | */ 1363 | WorkerDirector.prototype.getMaxQueueSize = function () { 1364 | return this.maxQueueSize; 1365 | }; 1366 | 1367 | /** 1368 | * Returns the maximum number of workers. 1369 | * @memberOf THREE.LoaderSupport.WorkerDirector 1370 | * 1371 | * @returns {number} 1372 | */ 1373 | WorkerDirector.prototype.getMaxWebWorkers = function () { 1374 | return this.maxWebWorkers; 1375 | }; 1376 | 1377 | /** 1378 | * Sets the CORS string to be used. 1379 | * @memberOf THREE.LoaderSupport.WorkerDirector 1380 | * 1381 | * @param {string} crossOrigin CORS value 1382 | */ 1383 | WorkerDirector.prototype.setCrossOrigin = function ( crossOrigin ) { 1384 | this.crossOrigin = crossOrigin; 1385 | }; 1386 | 1387 | /** 1388 | * Create or destroy workers according limits. Set the name and register callbacks for dynamically created web workers. 1389 | * @memberOf THREE.LoaderSupport.WorkerDirector 1390 | * 1391 | * @param {THREE.OBJLoader2.WWOBJLoader2.PrepDataCallbacks} globalCallbacks Register global callbacks used by all web workers 1392 | * @param {number} maxQueueSize Set the maximum size of the instruction queue (1-1024) 1393 | * @param {number} maxWebWorkers Set the maximum amount of workers (1-16) 1394 | */ 1395 | WorkerDirector.prototype.prepareWorkers = function ( globalCallbacks, maxQueueSize, maxWebWorkers ) { 1396 | if ( Validator.isValid( globalCallbacks ) ) this.workerDescription.globalCallbacks = globalCallbacks; 1397 | this.maxQueueSize = Math.min( maxQueueSize, MAX_QUEUE_SIZE ); 1398 | this.maxWebWorkers = Math.min( maxWebWorkers, MAX_WEB_WORKER ); 1399 | this.maxWebWorkers = Math.min( this.maxWebWorkers, this.maxQueueSize ); 1400 | this.objectsCompleted = 0; 1401 | this.instructionQueue = []; 1402 | this.instructionQueuePointer = 0; 1403 | 1404 | for ( var instanceNo = 0; instanceNo < this.maxWebWorkers; instanceNo++ ) { 1405 | 1406 | this.workerDescription.workerSupports[ instanceNo ] = { 1407 | instanceNo: instanceNo, 1408 | inUse: false, 1409 | terminateRequested: false, 1410 | workerSupport: new THREE.LoaderSupport.WorkerSupport( this.logger ), 1411 | loader: null 1412 | }; 1413 | 1414 | } 1415 | }; 1416 | 1417 | /** 1418 | * Store run instructions in internal instructionQueue. 1419 | * @memberOf THREE.LoaderSupport.WorkerDirector 1420 | * 1421 | * @param {THREE.LoaderSupport.PrepData} prepData 1422 | */ 1423 | WorkerDirector.prototype.enqueueForRun = function ( prepData ) { 1424 | if ( this.instructionQueue.length < this.maxQueueSize ) { 1425 | this.instructionQueue.push( prepData ); 1426 | } 1427 | }; 1428 | 1429 | /** 1430 | * Returns if any workers are running. 1431 | * 1432 | * @memberOf THREE.LoaderSupport.WorkerDirector 1433 | * @returns {boolean} 1434 | */ 1435 | WorkerDirector.prototype.isRunning = function () { 1436 | var wsKeys = Object.keys( this.workerDescription.workerSupports ); 1437 | return ( ( this.instructionQueue.length > 0 && this.instructionQueuePointer < this.instructionQueue.length ) || wsKeys.length > 0 ); 1438 | }; 1439 | 1440 | /** 1441 | * Process the instructionQueue until it is depleted. 1442 | * @memberOf THREE.LoaderSupport.WorkerDirector 1443 | */ 1444 | WorkerDirector.prototype.processQueue = function () { 1445 | var prepData, supportDesc; 1446 | for ( var instanceNo in this.workerDescription.workerSupports ) { 1447 | 1448 | supportDesc = this.workerDescription.workerSupports[ instanceNo ]; 1449 | if ( ! supportDesc.inUse ) { 1450 | 1451 | if ( this.instructionQueuePointer < this.instructionQueue.length ) { 1452 | 1453 | prepData = this.instructionQueue[ this.instructionQueuePointer ]; 1454 | this._kickWorkerRun( prepData, supportDesc ); 1455 | this.instructionQueuePointer++; 1456 | 1457 | } else { 1458 | 1459 | this._deregister( supportDesc ); 1460 | 1461 | } 1462 | 1463 | } 1464 | 1465 | } 1466 | 1467 | if ( ! this.isRunning() && this.callbackOnFinishedProcessing !== null ) { 1468 | 1469 | this.callbackOnFinishedProcessing(); 1470 | this.callbackOnFinishedProcessing = null; 1471 | 1472 | } 1473 | }; 1474 | 1475 | WorkerDirector.prototype._kickWorkerRun = function( prepData, supportDesc ) { 1476 | supportDesc.inUse = true; 1477 | supportDesc.workerSupport.setTerminateRequested( supportDesc.terminateRequested ); 1478 | 1479 | this.logger.logInfo( '\nAssigning next item from queue to worker (queue length: ' + this.instructionQueue.length + ')\n\n' ); 1480 | 1481 | var scope = this; 1482 | var prepDataCallbacks = prepData.getCallbacks(); 1483 | var globalCallbacks = this.workerDescription.globalCallbacks; 1484 | var wrapperOnLoad = function ( event ) { 1485 | if ( Validator.isValid( globalCallbacks.onLoad ) ) globalCallbacks.onLoad( event ); 1486 | if ( Validator.isValid( prepDataCallbacks.onLoad ) ) prepDataCallbacks.onLoad( event ); 1487 | scope.objectsCompleted++; 1488 | supportDesc.inUse = false; 1489 | 1490 | scope.processQueue(); 1491 | }; 1492 | 1493 | var wrapperOnProgress = function ( event ) { 1494 | if ( Validator.isValid( globalCallbacks.onProgress ) ) globalCallbacks.onProgress( event ); 1495 | if ( Validator.isValid( prepDataCallbacks.onProgress ) ) prepDataCallbacks.onProgress( event ); 1496 | }; 1497 | 1498 | var wrapperOnMeshAlter = function ( event ) { 1499 | if ( Validator.isValid( globalCallbacks.onMeshAlter ) ) globalCallbacks.onMeshAlter( event ); 1500 | if ( Validator.isValid( prepDataCallbacks.onMeshAlter ) ) prepDataCallbacks.onMeshAlter( event ); 1501 | }; 1502 | 1503 | supportDesc.loader = this._buildLoader( supportDesc.instanceNo ); 1504 | 1505 | var updatedCallbacks = new THREE.LoaderSupport.Callbacks(); 1506 | updatedCallbacks.setCallbackOnLoad( wrapperOnLoad ); 1507 | updatedCallbacks.setCallbackOnProgress( wrapperOnProgress ); 1508 | updatedCallbacks.setCallbackOnMeshAlter( wrapperOnMeshAlter ); 1509 | prepData.callbacks = updatedCallbacks; 1510 | 1511 | supportDesc.loader.run( prepData, supportDesc.workerSupport ); 1512 | }; 1513 | 1514 | WorkerDirector.prototype._buildLoader = function ( instanceNo ) { 1515 | var classDef = this.workerDescription.classDef; 1516 | var loader = Object.create( classDef.prototype ); 1517 | this.workerDescription.classDef.call( loader, THREE.DefaultLoadingManager, this.logger ); 1518 | 1519 | // verify that all required functions are implemented 1520 | if ( ! loader.hasOwnProperty( 'instanceNo' ) ) throw classDef.name + ' has no property "instanceNo".'; 1521 | loader.instanceNo = instanceNo; 1522 | 1523 | if ( ! loader.hasOwnProperty( 'workerSupport' ) ) { 1524 | 1525 | throw classDef.name + ' has no property "workerSupport".'; 1526 | 1527 | } 1528 | if ( typeof loader.run !== 'function' ) throw classDef.name + ' has no function "run".'; 1529 | if ( ! loader.hasOwnProperty( 'callbacks' ) || ! Validator.isValid( loader.callbacks ) ) { 1530 | 1531 | this.logger.logWarn( classDef.name + ' has an invalid property "callbacks". Will change to "THREE.LoaderSupport.Callbacks"' ); 1532 | loader.callbacks = new THREE.LoaderSupport.Callbacks(); 1533 | 1534 | } 1535 | return loader; 1536 | }; 1537 | 1538 | WorkerDirector.prototype._deregister = function ( supportDesc ) { 1539 | if ( Validator.isValid( supportDesc ) ) { 1540 | 1541 | supportDesc.workerSupport.setTerminateRequested( true ); 1542 | this.logger.logInfo( 'Requested termination of worker #' + supportDesc.instanceNo + '.' ); 1543 | 1544 | var loaderCallbacks = supportDesc.loader.callbacks; 1545 | if ( Validator.isValid( loaderCallbacks.onProgress ) ) loaderCallbacks.onProgress( { detail: { text: '' } } ); 1546 | delete this.workerDescription.workerSupports[ supportDesc.instanceNo ]; 1547 | 1548 | } 1549 | }; 1550 | 1551 | /** 1552 | * Terminate all workers. 1553 | * @memberOf THREE.LoaderSupport.WorkerDirector 1554 | * 1555 | * @param {callback} callbackOnFinishedProcessing Function called once all workers finished processing. 1556 | */ 1557 | WorkerDirector.prototype.tearDown = function ( callbackOnFinishedProcessing ) { 1558 | this.logger.logInfo( 'WorkerDirector received the deregister call. Terminating all workers!' ); 1559 | 1560 | this.instructionQueuePointer = this.instructionQueue.length; 1561 | this.callbackOnFinishedProcessing = Validator.verifyInput( callbackOnFinishedProcessing, null ); 1562 | 1563 | for ( var name in this.workerDescription.workerSupports ) { 1564 | 1565 | this.workerDescription.workerSupports[ name ].terminateRequested = true; 1566 | 1567 | } 1568 | }; 1569 | 1570 | return WorkerDirector; 1571 | 1572 | })(); 1573 | --------------------------------------------------------------------------------