UNPKG

bytev-charts

Version:

基于echarts和JavaScript及ES6封装的一个可以直接调用的图表组件库,内置主题设计,简单快捷,且支持用户自定义配置; npm 安装方式: npm install bytev-charts 若启动提示还需额外install插件,则运行 npm install @babel/runtime-corejs2 即可;

1,493 lines (1,216 loc) 85 kB
import "core-js/modules/es.regexp.constructor.js"; import "core-js/modules/es.regexp.exec.js"; import "core-js/modules/es.regexp.to-string.js"; import "core-js/modules/es.array.join.js"; import "core-js/modules/es.function.name.js"; import "core-js/modules/es.array.iterator.js"; import "core-js/modules/es.object.to-string.js"; import "core-js/modules/web.dom-collections.iterator.js"; import "core-js/modules/es.string.replace.js"; import "core-js/modules/es.array.for-each.js"; import "core-js/modules/web.dom-collections.for-each.js"; import "core-js/modules/es.array.map.js"; import "core-js/modules/es.string.repeat.js"; import "core-js/modules/es.array-buffer.slice.js"; import "core-js/modules/es.typed-array.uint8-array.js"; import "core-js/modules/es.typed-array.copy-within.js"; import "core-js/modules/es.typed-array.every.js"; import "core-js/modules/es.typed-array.fill.js"; import "core-js/modules/es.typed-array.filter.js"; import "core-js/modules/es.typed-array.find.js"; import "core-js/modules/es.typed-array.find-index.js"; import "core-js/modules/es.typed-array.for-each.js"; import "core-js/modules/es.typed-array.includes.js"; import "core-js/modules/es.typed-array.index-of.js"; import "core-js/modules/es.typed-array.iterator.js"; import "core-js/modules/es.typed-array.join.js"; import "core-js/modules/es.typed-array.last-index-of.js"; import "core-js/modules/es.typed-array.map.js"; import "core-js/modules/es.typed-array.reduce.js"; import "core-js/modules/es.typed-array.reduce-right.js"; import "core-js/modules/es.typed-array.reverse.js"; import "core-js/modules/es.typed-array.set.js"; import "core-js/modules/es.typed-array.slice.js"; import "core-js/modules/es.typed-array.some.js"; import "core-js/modules/es.typed-array.sort.js"; import "core-js/modules/es.typed-array.subarray.js"; import "core-js/modules/es.typed-array.to-locale-string.js"; import "core-js/modules/es.typed-array.to-string.js"; import "core-js/modules/es.typed-array.float32-array.js"; import "core-js/modules/es.array.index-of.js"; import _Object$assign from "@babel/runtime-corejs2/core-js/object/assign"; import _Object$create from "@babel/runtime-corejs2/core-js/object/create"; import _parseFloat from "@babel/runtime-corejs2/core-js/parse-float"; import _parseInt from "@babel/runtime-corejs2/core-js/parse-int"; console.warn("THREE.VRMLLoader: As part of the transition to ES6 Modules, the files in 'examples/js' were deprecated in May 2020 (r117) and will be deleted in December 2020 (r124). You can find more information about developing using ES6 Modules in https://threejs.org/docs/#manual/en/introduction/Installation."); /* global chevrotain */ THREE.VRMLLoader = function () { // dependency check if (typeof chevrotain === 'undefined') { throw Error('THREE.VRMLLoader: External library chevrotain.min.js required.'); } // class definitions function VRMLLoader(manager) { THREE.Loader.call(this, manager); } VRMLLoader.prototype = _Object$assign(_Object$create(THREE.Loader.prototype), { constructor: VRMLLoader, load: function load(url, onLoad, onProgress, onError) { var scope = this; var path = scope.path === '' ? THREE.LoaderUtils.extractUrlBase(url) : scope.path; var loader = new THREE.FileLoader(scope.manager); loader.setPath(scope.path); loader.setRequestHeader(scope.requestHeader); loader.load(url, function (text) { try { onLoad(scope.parse(text, path)); } catch (e) { if (onError) { onError(e); } else { console.error(e); } scope.manager.itemError(url); } }, onProgress, onError); }, parse: function parse(data, path) { var nodeMap = {}; function generateVRMLTree(data) { // create lexer, parser and visitor var tokenData = createTokens(); var lexer = new VRMLLexer(tokenData.tokens); var parser = new VRMLParser(tokenData.tokenVocabulary); var visitor = createVisitor(parser.getBaseCstVisitorConstructor()); // lexing var lexingResult = lexer.lex(data); parser.input = lexingResult.tokens; // parsing var cstOutput = parser.vrml(); if (parser.errors.length > 0) { console.error(parser.errors); throw Error('THREE.VRMLLoader: Parsing errors detected.'); } // actions var ast = visitor.visit(cstOutput); return ast; } function createTokens() { var createToken = chevrotain.createToken; // from http://gun.teipir.gr/VRML-amgem/spec/part1/concepts.html#SyntaxBasics var RouteIdentifier = createToken({ name: 'RouteIdentifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*[\.][^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/ }); var Identifier = createToken({ name: 'Identifier', pattern: /[^\x30-\x39\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d][^\0-\x20\x22\x27\x23\x2b\x2c\x2d\x2e\x5b\x5d\x5c\x7b\x7d]*/, longer_alt: RouteIdentifier }); // from http://gun.teipir.gr/VRML-amgem/spec/part1/nodesRef.html var nodeTypes = ['Anchor', 'Billboard', 'Collision', 'Group', 'Transform', // grouping nodes 'Inline', 'LOD', 'Switch', // special groups 'AudioClip', 'DirectionalLight', 'PointLight', 'Script', 'Shape', 'Sound', 'SpotLight', 'WorldInfo', // common nodes 'CylinderSensor', 'PlaneSensor', 'ProximitySensor', 'SphereSensor', 'TimeSensor', 'TouchSensor', 'VisibilitySensor', // sensors 'Box', 'Cone', 'Cylinder', 'ElevationGrid', 'Extrusion', 'IndexedFaceSet', 'IndexedLineSet', 'PointSet', 'Sphere', // geometries 'Color', 'Coordinate', 'Normal', 'TextureCoordinate', // geometric properties 'Appearance', 'FontStyle', 'ImageTexture', 'Material', 'MovieTexture', 'PixelTexture', 'TextureTransform', // appearance 'ColorInterpolator', 'CoordinateInterpolator', 'NormalInterpolator', 'OrientationInterpolator', 'PositionInterpolator', 'ScalarInterpolator', // interpolators 'Background', 'Fog', 'NavigationInfo', 'Viewpoint', // bindable nodes 'Text' // Text must be placed at the end of the regex so there are no matches for TextureTransform and TextureCoordinate ]; // var Version = createToken({ name: 'Version', pattern: /#VRML.*/, longer_alt: Identifier }); var NodeName = createToken({ name: 'NodeName', pattern: new RegExp(nodeTypes.join('|')), longer_alt: Identifier }); var DEF = createToken({ name: 'DEF', pattern: /DEF/, longer_alt: Identifier }); var USE = createToken({ name: 'USE', pattern: /USE/, longer_alt: Identifier }); var ROUTE = createToken({ name: 'ROUTE', pattern: /ROUTE/, longer_alt: Identifier }); var TO = createToken({ name: 'TO', pattern: /TO/, longer_alt: Identifier }); // var StringLiteral = createToken({ name: "StringLiteral", pattern: /"(:?[^\\"\n\r]+|\\(:?[bfnrtv"\\/]|u[0-9a-fA-F]{4}))*"/ }); var HexLiteral = createToken({ name: 'HexLiteral', pattern: /0[xX][0-9a-fA-F]+/ }); var NumberLiteral = createToken({ name: 'NumberLiteral', pattern: /[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/ }); var TrueLiteral = createToken({ name: 'TrueLiteral', pattern: /TRUE/ }); var FalseLiteral = createToken({ name: 'FalseLiteral', pattern: /FALSE/ }); var NullLiteral = createToken({ name: 'NullLiteral', pattern: /NULL/ }); var LSquare = createToken({ name: 'LSquare', pattern: /\[/ }); var RSquare = createToken({ name: 'RSquare', pattern: /]/ }); var LCurly = createToken({ name: 'LCurly', pattern: /{/ }); var RCurly = createToken({ name: 'RCurly', pattern: /}/ }); var Comment = createToken({ name: 'Comment', pattern: /#.*/, group: chevrotain.Lexer.SKIPPED }); // commas, blanks, tabs, newlines and carriage returns are whitespace characters wherever they appear outside of string fields var WhiteSpace = createToken({ name: 'WhiteSpace', pattern: /[ ,\s]/, group: chevrotain.Lexer.SKIPPED }); var tokens = [WhiteSpace, // keywords appear before the Identifier NodeName, DEF, USE, ROUTE, TO, TrueLiteral, FalseLiteral, NullLiteral, // the Identifier must appear after the keywords because all keywords are valid identifiers Version, Identifier, RouteIdentifier, StringLiteral, HexLiteral, NumberLiteral, LSquare, RSquare, LCurly, RCurly, Comment]; var tokenVocabulary = {}; for (var i = 0, l = tokens.length; i < l; i++) { var token = tokens[i]; tokenVocabulary[token.name] = token; } return { tokens: tokens, tokenVocabulary: tokenVocabulary }; } function createVisitor(BaseVRMLVisitor) { // the visitor is created dynmaically based on the given base class function VRMLToASTVisitor() { BaseVRMLVisitor.call(this); this.validateVisitor(); } VRMLToASTVisitor.prototype = _Object$assign(_Object$create(BaseVRMLVisitor.prototype), { constructor: VRMLToASTVisitor, vrml: function vrml(ctx) { var data = { version: this.visit(ctx.version), nodes: [], routes: [] }; for (var i = 0, l = ctx.node.length; i < l; i++) { var node = ctx.node[i]; data.nodes.push(this.visit(node)); } if (ctx.route) { for (var i = 0, l = ctx.route.length; i < l; i++) { var route = ctx.route[i]; data.routes.push(this.visit(route)); } } return data; }, version: function version(ctx) { return ctx.Version[0].image; }, node: function node(ctx) { var data = { name: ctx.NodeName[0].image, fields: [] }; if (ctx.field) { for (var i = 0, l = ctx.field.length; i < l; i++) { var field = ctx.field[i]; data.fields.push(this.visit(field)); } } // DEF if (ctx.def) { data.DEF = this.visit(ctx.def[0]); } return data; }, field: function field(ctx) { var data = { name: ctx.Identifier[0].image, type: null, values: null }; var result; // SFValue if (ctx.singleFieldValue) { result = this.visit(ctx.singleFieldValue[0]); } // MFValue if (ctx.multiFieldValue) { result = this.visit(ctx.multiFieldValue[0]); } data.type = result.type; data.values = result.values; return data; }, def: function def(ctx) { return (ctx.Identifier || ctx.NodeName)[0].image; }, use: function use(ctx) { return { USE: (ctx.Identifier || ctx.NodeName)[0].image }; }, singleFieldValue: function singleFieldValue(ctx) { return processField(this, ctx); }, multiFieldValue: function multiFieldValue(ctx) { return processField(this, ctx); }, route: function route(ctx) { var data = { FROM: ctx.RouteIdentifier[0].image, TO: ctx.RouteIdentifier[1].image }; return data; } }); function processField(scope, ctx) { var field = { type: null, values: [] }; if (ctx.node) { field.type = 'node'; for (var i = 0, l = ctx.node.length; i < l; i++) { var node = ctx.node[i]; field.values.push(scope.visit(node)); } } if (ctx.use) { field.type = 'use'; for (var i = 0, l = ctx.use.length; i < l; i++) { var use = ctx.use[i]; field.values.push(scope.visit(use)); } } if (ctx.StringLiteral) { field.type = 'string'; for (var i = 0, l = ctx.StringLiteral.length; i < l; i++) { var stringLiteral = ctx.StringLiteral[i]; field.values.push(stringLiteral.image.replace(/'|"/g, '')); } } if (ctx.NumberLiteral) { field.type = 'number'; for (var i = 0, l = ctx.NumberLiteral.length; i < l; i++) { var numberLiteral = ctx.NumberLiteral[i]; field.values.push(_parseFloat(numberLiteral.image)); } } if (ctx.HexLiteral) { field.type = 'hex'; for (var i = 0, l = ctx.HexLiteral.length; i < l; i++) { var hexLiteral = ctx.HexLiteral[i]; field.values.push(hexLiteral.image); } } if (ctx.TrueLiteral) { field.type = 'boolean'; for (var i = 0, l = ctx.TrueLiteral.length; i < l; i++) { var trueLiteral = ctx.TrueLiteral[i]; if (trueLiteral.image === 'TRUE') field.values.push(true); } } if (ctx.FalseLiteral) { field.type = 'boolean'; for (var i = 0, l = ctx.FalseLiteral.length; i < l; i++) { var falseLiteral = ctx.FalseLiteral[i]; if (falseLiteral.image === 'FALSE') field.values.push(false); } } if (ctx.NullLiteral) { field.type = 'null'; ctx.NullLiteral.forEach(function () { field.values.push(null); }); } return field; } return new VRMLToASTVisitor(); } function parseTree(tree) { // console.log( JSON.stringify( tree, null, 2 ) ); var nodes = tree.nodes; var scene = new THREE.Scene(); // first iteration: build nodemap based on DEF statements for (var i = 0, l = nodes.length; i < l; i++) { var node = nodes[i]; buildNodeMap(node); } // second iteration: build nodes for (var i = 0, l = nodes.length; i < l; i++) { var node = nodes[i]; var object = getNode(node); if (object instanceof THREE.Object3D) scene.add(object); if (node.name === 'WorldInfo') scene.userData.worldInfo = object; } return scene; } function buildNodeMap(node) { if (node.DEF) { nodeMap[node.DEF] = node; } var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; if (field.type === 'node') { var fieldValues = field.values; for (var j = 0, jl = fieldValues.length; j < jl; j++) { buildNodeMap(fieldValues[j]); } } } } function getNode(node) { // handle case where a node refers to a different one if (node.USE) { return resolveUSE(node.USE); } if (node.build !== undefined) return node.build; node.build = buildNode(node); return node.build; } // node builder function buildNode(node) { var nodeName = node.name; var build; switch (nodeName) { case 'Group': case 'Transform': case 'Collision': build = buildGroupingNode(node); break; case 'Background': build = buildBackgroundNode(node); break; case 'Shape': build = buildShapeNode(node); break; case 'Appearance': build = buildAppearanceNode(node); break; case 'Material': build = buildMaterialNode(node); break; case 'ImageTexture': build = buildImageTextureNode(node); break; case 'PixelTexture': build = buildPixelTextureNode(node); break; case 'TextureTransform': build = buildTextureTransformNode(node); break; case 'IndexedFaceSet': build = buildIndexedFaceSetNode(node); break; case 'IndexedLineSet': build = buildIndexedLineSetNode(node); break; case 'PointSet': build = buildPointSetNode(node); break; case 'Box': build = buildBoxNode(node); break; case 'Cone': build = buildConeNode(node); break; case 'Cylinder': build = buildCylinderNode(node); break; case 'Sphere': build = buildSphereNode(node); break; case 'ElevationGrid': build = buildElevationGridNode(node); break; case 'Extrusion': build = buildExtrusionNode(node); break; case 'Color': case 'Coordinate': case 'Normal': case 'TextureCoordinate': build = buildGeometricNode(node); break; case 'WorldInfo': build = buildWorldInfoNode(node); break; case 'Anchor': case 'Billboard': case 'Inline': case 'LOD': case 'Switch': case 'AudioClip': case 'DirectionalLight': case 'PointLight': case 'Script': case 'Sound': case 'SpotLight': case 'CylinderSensor': case 'PlaneSensor': case 'ProximitySensor': case 'SphereSensor': case 'TimeSensor': case 'TouchSensor': case 'VisibilitySensor': case 'Text': case 'FontStyle': case 'MovieTexture': case 'ColorInterpolator': case 'CoordinateInterpolator': case 'NormalInterpolator': case 'OrientationInterpolator': case 'PositionInterpolator': case 'ScalarInterpolator': case 'Fog': case 'NavigationInfo': case 'Viewpoint': // node not supported yet break; default: console.warn('THREE.VRMLLoader: Unknown node:', nodeName); break; } return build; } function buildGroupingNode(node) { var object = new THREE.Group(); // var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'bboxCenter': // field not supported break; case 'bboxSize': // field not supported break; case 'center': // field not supported break; case 'children': parseFieldChildren(fieldValues, object); break; case 'collide': // field not supported break; case 'rotation': var axis = new THREE.Vector3(fieldValues[0], fieldValues[1], fieldValues[2]); var angle = fieldValues[3]; object.quaternion.setFromAxisAngle(axis, angle); break; case 'scale': object.scale.set(fieldValues[0], fieldValues[1], fieldValues[2]); break; case 'scaleOrientation': // field not supported break; case 'translation': object.position.set(fieldValues[0], fieldValues[1], fieldValues[2]); break; case 'proxy': // field not supported break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } return object; } function buildBackgroundNode(node) { var group = new THREE.Group(); var groundAngle, groundColor; var skyAngle, skyColor; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'groundAngle': groundAngle = fieldValues; break; case 'groundColor': groundColor = fieldValues; break; case 'backUrl': // field not supported break; case 'bottomUrl': // field not supported break; case 'frontUrl': // field not supported break; case 'leftUrl': // field not supported break; case 'rightUrl': // field not supported break; case 'topUrl': // field not supported break; case 'skyAngle': skyAngle = fieldValues; break; case 'skyColor': skyColor = fieldValues; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } var radius = 10000; // sky if (skyColor) { var skyGeometry = new THREE.SphereBufferGeometry(radius, 32, 16); var skyMaterial = new THREE.MeshBasicMaterial({ fog: false, side: THREE.BackSide, depthWrite: false, depthTest: false }); if (skyColor.length > 3) { paintFaces(skyGeometry, radius, skyAngle, toColorArray(skyColor), true); skyMaterial.vertexColors = true; } else { skyMaterial.color.setRGB(skyColor[0], skyColor[1], skyColor[2]); } var sky = new THREE.Mesh(skyGeometry, skyMaterial); group.add(sky); } // ground if (groundColor) { if (groundColor.length > 0) { var groundGeometry = new THREE.SphereBufferGeometry(radius, 32, 16, 0, 2 * Math.PI, 0.5 * Math.PI, 1.5 * Math.PI); var groundMaterial = new THREE.MeshBasicMaterial({ fog: false, side: THREE.BackSide, vertexColors: true, depthWrite: false, depthTest: false }); paintFaces(groundGeometry, radius, groundAngle, toColorArray(groundColor), false); var ground = new THREE.Mesh(groundGeometry, groundMaterial); group.add(ground); } } // render background group first group.renderOrder = -Infinity; return group; } function buildShapeNode(node) { var fields = node.fields; // if the appearance field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0) var material = new THREE.MeshBasicMaterial({ color: 0x000000 }); var geometry; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'appearance': if (fieldValues[0] !== null) { material = getNode(fieldValues[0]); } break; case 'geometry': if (fieldValues[0] !== null) { geometry = getNode(fieldValues[0]); } break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } // build 3D object var object; if (geometry && geometry.attributes.position) { var type = geometry._type; if (type === 'points') { // points var pointsMaterial = new THREE.PointsMaterial({ color: 0xffffff }); if (geometry.attributes.color !== undefined) { pointsMaterial.vertexColors = true; } else { // if the color field is NULL and there is a material defined for the appearance affecting this PointSet, then use the emissiveColor of the material to draw the points if (material.isMeshPhongMaterial) { pointsMaterial.color.copy(material.emissive); } } object = new THREE.Points(geometry, pointsMaterial); } else if (type === 'line') { // lines var lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff }); if (geometry.attributes.color !== undefined) { lineMaterial.vertexColors = true; } else { // if the color field is NULL and there is a material defined for the appearance affecting this IndexedLineSet, then use the emissiveColor of the material to draw the lines if (material.isMeshPhongMaterial) { lineMaterial.color.copy(material.emissive); } } object = new THREE.LineSegments(geometry, lineMaterial); } else { // consider meshes // check "solid" hint (it's placed in the geometry but affects the material) if (geometry._solid !== undefined) { material.side = geometry._solid ? THREE.FrontSide : THREE.DoubleSide; } // check for vertex colors if (geometry.attributes.color !== undefined) { material.vertexColors = true; } object = new THREE.Mesh(geometry, material); } } else { object = new THREE.Object3D(); // if the geometry field is NULL or no vertices are defined the object is not drawn object.visible = false; } return object; } function buildAppearanceNode(node) { var material = new THREE.MeshPhongMaterial(); var transformData; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'material': if (fieldValues[0] !== null) { var materialData = getNode(fieldValues[0]); if (materialData.diffuseColor) material.color.copy(materialData.diffuseColor); if (materialData.emissiveColor) material.emissive.copy(materialData.emissiveColor); if (materialData.shininess) material.shininess = materialData.shininess; if (materialData.specularColor) material.specular.copy(materialData.specularColor); if (materialData.transparency) material.opacity = 1 - materialData.transparency; if (materialData.transparency > 0) material.transparent = true; } else { // if the material field is NULL or unspecified, lighting is off and the unlit object color is (0, 0, 0) material = new THREE.MeshBasicMaterial({ color: 0x000000 }); } break; case 'texture': var textureNode = fieldValues[0]; if (textureNode !== null) { if (textureNode.name === 'ImageTexture' || textureNode.name === 'PixelTexture') { material.map = getNode(textureNode); } else {// MovieTexture not supported yet } } break; case 'textureTransform': if (fieldValues[0] !== null) { transformData = getNode(fieldValues[0]); } break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } // only apply texture transform data if a texture was defined if (material.map) { // respect VRML lighting model if (material.map.__type) { switch (material.map.__type) { case TEXTURE_TYPE.INTENSITY_ALPHA: material.opacity = 1; // ignore transparency break; case TEXTURE_TYPE.RGB: material.color.set(0xffffff); // ignore material color break; case TEXTURE_TYPE.RGBA: material.color.set(0xffffff); // ignore material color material.opacity = 1; // ignore transparency break; default: } delete material.map.__type; } // apply texture transform if (transformData) { material.map.center.copy(transformData.center); material.map.rotation = transformData.rotation; material.map.repeat.copy(transformData.scale); material.map.offset.copy(transformData.translation); } } return material; } function buildMaterialNode(node) { var materialData = {}; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'ambientIntensity': // field not supported break; case 'diffuseColor': materialData.diffuseColor = new THREE.Color(fieldValues[0], fieldValues[1], fieldValues[2]); break; case 'emissiveColor': materialData.emissiveColor = new THREE.Color(fieldValues[0], fieldValues[1], fieldValues[2]); break; case 'shininess': materialData.shininess = fieldValues[0]; break; case 'specularColor': materialData.emissiveColor = new THREE.Color(fieldValues[0], fieldValues[1], fieldValues[2]); break; case 'transparency': materialData.transparency = fieldValues[0]; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } return materialData; } function parseHexColor(hex, textureType, color) { switch (textureType) { case TEXTURE_TYPE.INTENSITY: // Intensity texture: A one-component image specifies one-byte hexadecimal or integer values representing the intensity of the image var value = _parseInt(hex); color.r = value; color.g = value; color.b = value; break; case TEXTURE_TYPE.INTENSITY_ALPHA: // Intensity+Alpha texture: A two-component image specifies the intensity in the first (high) byte and the alpha opacity in the second (low) byte. var value = _parseInt("0x" + hex.substring(2, 4)); color.r = value; color.g = value; color.b = value; color.a = _parseInt("0x" + hex.substring(4, 6)); break; case TEXTURE_TYPE.RGB: // RGB texture: Pixels in a three-component image specify the red component in the first (high) byte, followed by the green and blue components color.r = _parseInt("0x" + hex.substring(2, 4)); color.g = _parseInt("0x" + hex.substring(4, 6)); color.b = _parseInt("0x" + hex.substring(6, 8)); break; case TEXTURE_TYPE.RGBA: // RGBA texture: Four-component images specify the alpha opacity byte after red/green/blue color.r = _parseInt("0x" + hex.substring(2, 4)); color.g = _parseInt("0x" + hex.substring(4, 6)); color.b = _parseInt("0x" + hex.substring(6, 8)); color.a = _parseInt("0x" + hex.substring(8, 10)); break; default: } } function getTextureType(num_components) { var type; switch (num_components) { case 1: type = TEXTURE_TYPE.INTENSITY; break; case 2: type = TEXTURE_TYPE.INTENSITY_ALPHA; break; case 3: type = TEXTURE_TYPE.RGB; break; case 4: type = TEXTURE_TYPE.RGBA; break; default: } return type; } function buildPixelTextureNode(node) { var texture; var wrapS = THREE.RepeatWrapping; var wrapT = THREE.RepeatWrapping; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'image': var width = fieldValues[0]; var height = fieldValues[1]; var num_components = fieldValues[2]; var useAlpha = num_components === 2 || num_components === 4; var textureType = getTextureType(num_components); var size = (useAlpha === true ? 4 : 3) * (width * height); var data = new Uint8Array(size); var color = { r: 0, g: 0, b: 0, a: 0 }; for (var j = 3, k = 0, jl = fieldValues.length; j < jl; j++, k++) { parseHexColor(fieldValues[j], textureType, color); if (useAlpha === true) { var stride = k * 4; data[stride + 0] = color.r; data[stride + 1] = color.g; data[stride + 2] = color.b; data[stride + 3] = color.a; } else { var stride = k * 3; data[stride + 0] = color.r; data[stride + 1] = color.g; data[stride + 2] = color.b; } } texture = new THREE.DataTexture(data, width, height, useAlpha === true ? THREE.RGBAFormat : THREE.RGBFormat); texture.__type = textureType; // needed for material modifications break; case 'repeatS': if (fieldValues[0] === false) wrapS = THREE.ClampToEdgeWrapping; break; case 'repeatT': if (fieldValues[0] === false) wrapT = THREE.ClampToEdgeWrapping; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } if (texture) { texture.wrapS = wrapS; texture.wrapT = wrapT; } return texture; } function buildImageTextureNode(node) { var texture; var wrapS = THREE.RepeatWrapping; var wrapT = THREE.RepeatWrapping; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'url': var url = fieldValues[0]; if (url) texture = textureLoader.load(url); break; case 'repeatS': if (fieldValues[0] === false) wrapS = THREE.ClampToEdgeWrapping; break; case 'repeatT': if (fieldValues[0] === false) wrapT = THREE.ClampToEdgeWrapping; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } if (texture) { texture.wrapS = wrapS; texture.wrapT = wrapT; } return texture; } function buildTextureTransformNode(node) { var transformData = { center: new THREE.Vector2(), rotation: new THREE.Vector2(), scale: new THREE.Vector2(), translation: new THREE.Vector2() }; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'center': transformData.center.set(fieldValues[0], fieldValues[1]); break; case 'rotation': transformData.rotation = fieldValues[0]; break; case 'scale': transformData.scale.set(fieldValues[0], fieldValues[1]); break; case 'translation': transformData.translation.set(fieldValues[0], fieldValues[1]); break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } return transformData; } function buildGeometricNode(node) { return node.fields[0].values; } function buildWorldInfoNode(node) { var worldInfo = {}; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'title': worldInfo.title = fieldValues[0]; break; case 'info': worldInfo.info = fieldValues; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } return worldInfo; } function buildIndexedFaceSetNode(node) { var color, coord, normal, texCoord; var ccw = true, solid = true, creaseAngle = 0; var colorIndex, coordIndex, normalIndex, texCoordIndex; var colorPerVertex = true, normalPerVertex = true; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'color': var colorNode = fieldValues[0]; if (colorNode !== null) { color = getNode(colorNode); } break; case 'coord': var coordNode = fieldValues[0]; if (coordNode !== null) { coord = getNode(coordNode); } break; case 'normal': var normalNode = fieldValues[0]; if (normalNode !== null) { normal = getNode(normalNode); } break; case 'texCoord': var texCoordNode = fieldValues[0]; if (texCoordNode !== null) { texCoord = getNode(texCoordNode); } break; case 'ccw': ccw = fieldValues[0]; break; case 'colorIndex': colorIndex = fieldValues; break; case 'colorPerVertex': colorPerVertex = fieldValues[0]; break; case 'convex': // field not supported break; case 'coordIndex': coordIndex = fieldValues; break; case 'creaseAngle': creaseAngle = fieldValues[0]; break; case 'normalIndex': normalIndex = fieldValues; break; case 'normalPerVertex': normalPerVertex = fieldValues[0]; break; case 'solid': solid = fieldValues[0]; break; case 'texCoordIndex': texCoordIndex = fieldValues; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } if (coordIndex === undefined) { console.warn('THREE.VRMLLoader: Missing coordIndex.'); return new THREE.BufferGeometry(); // handle VRML files with incomplete geometry definition } var triangulatedCoordIndex = triangulateFaceIndex(coordIndex, ccw); var positionAttribute; var colorAttribute; var normalAttribute; var uvAttribute; if (color) { if (colorPerVertex === true) { if (colorIndex && colorIndex.length > 0) { // if the colorIndex field is not empty, then it is used to choose colors for each vertex of the IndexedFaceSet. var triangulatedColorIndex = triangulateFaceIndex(colorIndex, ccw); colorAttribute = computeAttributeFromIndexedData(triangulatedCoordIndex, triangulatedColorIndex, color, 3); } else { // if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node colorAttribute = toNonIndexedAttribute(triangulatedCoordIndex, new THREE.Float32BufferAttribute(color, 3)); } } else { if (colorIndex && colorIndex.length > 0) { // if the colorIndex field is not empty, then they are used to choose one color for each face of the IndexedFaceSet var flattenFaceColors = flattenData(color, colorIndex); var triangulatedFaceColors = triangulateFaceData(flattenFaceColors, coordIndex); colorAttribute = computeAttributeFromFaceData(triangulatedCoordIndex, triangulatedFaceColors); } else { // if the colorIndex field is empty, then the color are applied to each face of the IndexedFaceSet in order var triangulatedFaceColors = triangulateFaceData(color, coordIndex); colorAttribute = computeAttributeFromFaceData(triangulatedCoordIndex, triangulatedFaceColors); } } } if (normal) { if (normalPerVertex === true) { // consider vertex normals if (normalIndex && normalIndex.length > 0) { // if the normalIndex field is not empty, then it is used to choose normals for each vertex of the IndexedFaceSet. var triangulatedNormalIndex = triangulateFaceIndex(normalIndex, ccw); normalAttribute = computeAttributeFromIndexedData(triangulatedCoordIndex, triangulatedNormalIndex, normal, 3); } else { // if the normalIndex field is empty, then the coordIndex field is used to choose normals from the Normal node normalAttribute = toNonIndexedAttribute(triangulatedCoordIndex, new THREE.Float32BufferAttribute(normal, 3)); } } else { // consider face normals if (normalIndex && normalIndex.length > 0) { // if the normalIndex field is not empty, then they are used to choose one normal for each face of the IndexedFaceSet var flattenFaceNormals = flattenData(normal, normalIndex); var triangulatedFaceNormals = triangulateFaceData(flattenFaceNormals, coordIndex); normalAttribute = computeAttributeFromFaceData(triangulatedCoordIndex, triangulatedFaceNormals); } else { // if the normalIndex field is empty, then the normals are applied to each face of the IndexedFaceSet in order var triangulatedFaceNormals = triangulateFaceData(normal, coordIndex); normalAttribute = computeAttributeFromFaceData(triangulatedCoordIndex, triangulatedFaceNormals); } } } else { // if the normal field is NULL, then the loader should automatically generate normals, using creaseAngle to determine if and how normals are smoothed across shared vertices normalAttribute = computeNormalAttribute(triangulatedCoordIndex, coord, creaseAngle); } if (texCoord) { // texture coordinates are always defined on vertex level if (texCoordIndex && texCoordIndex.length > 0) { // if the texCoordIndex field is not empty, then it is used to choose texture coordinates for each vertex of the IndexedFaceSet. var triangulatedTexCoordIndex = triangulateFaceIndex(texCoordIndex, ccw); uvAttribute = computeAttributeFromIndexedData(triangulatedCoordIndex, triangulatedTexCoordIndex, texCoord, 2); } else { // if the texCoordIndex field is empty, then the coordIndex array is used to choose texture coordinates from the TextureCoordinate node uvAttribute = toNonIndexedAttribute(triangulatedCoordIndex, new THREE.Float32BufferAttribute(texCoord, 2)); } } var geometry = new THREE.BufferGeometry(); positionAttribute = toNonIndexedAttribute(triangulatedCoordIndex, new THREE.Float32BufferAttribute(coord, 3)); geometry.setAttribute('position', positionAttribute); geometry.setAttribute('normal', normalAttribute); // optional attributes if (colorAttribute) geometry.setAttribute('color', colorAttribute); if (uvAttribute) geometry.setAttribute('uv', uvAttribute); // "solid" influences the material so let's store it for later use geometry._solid = solid; geometry._type = 'mesh'; return geometry; } function buildIndexedLineSetNode(node) { var color, coord; var colorIndex, coordIndex; var colorPerVertex = true; var fields = node.fields; for (var i = 0, l = fields.length; i < l; i++) { var field = fields[i]; var fieldName = field.name; var fieldValues = field.values; switch (fieldName) { case 'color': var colorNode = fieldValues[0]; if (colorNode !== null) { color = getNode(colorNode); } break; case 'coord': var coordNode = fieldValues[0]; if (coordNode !== null) { coord = getNode(coordNode); } break; case 'colorIndex': colorIndex = fieldValues; break; case 'colorPerVertex': colorPerVertex = fieldValues[0]; break; case 'coordIndex': coordIndex = fieldValues; break; default: console.warn('THREE.VRMLLoader: Unknown field:', fieldName); break; } } // build lines var colorAttribute; var expandedLineIndex = expandLineIndex(coordIndex); // create an index for three.js's linesegment primitive if (color) { if (colorPerVertex === true) { if (colorIndex.length > 0) { // if the colorIndex field is not empty, then one color is used for each polyline of the IndexedLineSet. var expandedColorIndex = expandLineIndex(colorIndex); // compute colors for each line segment (rendering primitve) colorAttribute = computeAttributeFromIndexedData(expandedLineIndex, expandedColorIndex, color, 3); // compute data on vertex level } else { // if the colorIndex field is empty, then the colors are applied to each polyline of the IndexedLineSet in order. colorAttribute = toNonIndexedAttribute(expandedLineIndex, new THREE.Float32BufferAttribute(color, 3)); } } else { if (colorIndex.length > 0) { // if the colorIndex field is not empty, then colors are applied to each vertex of the IndexedLineSet var flattenLineColors = flattenData(color, colorIndex); // compute colors for each VRML primitve var expandedLineColors = expandLineData(flattenLineColors, coordIndex); // compute colors for each line segment (rendering primitve) colorAttribute = computeAttributeFromLineData(expandedLineIndex, expandedLineColors); // compute data on vertex level } else { // if the colorIndex field is empty, then the coordIndex field is used to choose colors from the Color node var expandedLineColors = expandLineData(color, coordIndex); // compute colors for each line segment (rendering primitve) colorAttribute = computeAttributeFromLineData(expandedLineIndex, expandedLineColors