bytev-charts-beta
Version:
基于echarts和JavaScript及ES6封装的一个可以直接调用的图表组件库,内置主题设计,简单快捷,且支持用户自定义配置; npm 安装方式: npm install bytev-charts 若启动提示还需额外install插件,则运行 npm install @babel/runtime-corejs2 即可;
714 lines (624 loc) • 25.6 kB
JavaScript
import "core-js/modules/es.regexp.exec.js";
import "core-js/modules/es.string.split.js";
import "core-js/modules/es.array.for-each.js";
import "core-js/modules/es.object.to-string.js";
import "core-js/modules/web.dom-collections.for-each.js";
import "core-js/modules/es.function.name.js";
import "core-js/modules/es.array.map.js";
import "core-js/modules/es.array.filter.js";
import "core-js/modules/es.array.slice.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 _Array$isArray from "@babel/runtime-corejs2/core-js/array/is-array";
import _Array$from from "@babel/runtime-corejs2/core-js/array/from";
/**
* @version 1.1.1
*
* @desc Load files in LWO3 and LWO2 format on Three.js
*
* LWO3 format specification:
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo3.html
*
* LWO2 format specification:
* http://static.lightwave3d.com/sdk/2018/html/filefmts/lwo2.html
*
**/
import { AddOperation, BackSide, BufferAttribute, BufferGeometry, ClampToEdgeWrapping, Color, DoubleSide, EquirectangularReflectionMapping, EquirectangularRefractionMapping, FileLoader, Float32BufferAttribute, FrontSide, LineBasicMaterial, LineSegments, Loader, Mesh, MeshPhongMaterial, MeshPhysicalMaterial, MeshStandardMaterial, MirroredRepeatWrapping, Points, PointsMaterial, RepeatWrapping, TextureLoader, Vector2 } from "../../../build/three.module.js";
import { IFFParser } from "./lwo/IFFParser.js";
var lwoTree;
var LWOLoader = function LWOLoader(manager, parameters) {
Loader.call(this, manager);
parameters = parameters || {};
this.resourcePath = parameters.resourcePath !== undefined ? parameters.resourcePath : '';
};
LWOLoader.prototype = _Object$assign(_Object$create(Loader.prototype), {
constructor: LWOLoader,
load: function load(url, onLoad, onProgress, onError) {
var scope = this;
var path = scope.path === '' ? extractParentUrl(url, 'Objects') : scope.path; // give the mesh a default name based on the filename
var modelName = url.split(path).pop().split('.')[0];
var loader = new FileLoader(this.manager);
loader.setPath(scope.path);
loader.setResponseType('arraybuffer');
loader.load(url, function (buffer) {
// console.time( 'Total parsing: ' );
try {
onLoad(scope.parse(buffer, path, modelName));
} catch (e) {
if (onError) {
onError(e);
} else {
console.error(e);
}
scope.manager.itemError(url);
} // console.timeEnd( 'Total parsing: ' );
}, onProgress, onError);
},
parse: function parse(iffBuffer, path, modelName) {
lwoTree = new IFFParser().parse(iffBuffer); // console.log( 'lwoTree', lwoTree );
var textureLoader = new TextureLoader(this.manager).setPath(this.resourcePath || path).setCrossOrigin(this.crossOrigin);
return new LWOTreeParser(textureLoader).parse(modelName);
}
}); // Parse the lwoTree object
function LWOTreeParser(textureLoader) {
this.textureLoader = textureLoader;
}
LWOTreeParser.prototype = {
constructor: LWOTreeParser,
parse: function parse(modelName) {
this.materials = new MaterialParser(this.textureLoader).parse();
this.defaultLayerName = modelName;
this.meshes = this.parseLayers();
return {
materials: this.materials,
meshes: this.meshes
};
},
parseLayers: function parseLayers() {
// array of all meshes for building hierarchy
var meshes = []; // final array containing meshes with scene graph hierarchy set up
var finalMeshes = [];
var geometryParser = new GeometryParser();
var scope = this;
lwoTree.layers.forEach(function (layer) {
var geometry = geometryParser.parse(layer.geometry, layer);
var mesh = scope.parseMesh(geometry, layer);
meshes[layer.number] = mesh;
if (layer.parent === -1) finalMeshes.push(mesh);else meshes[layer.parent].add(mesh);
});
this.applyPivots(finalMeshes);
return finalMeshes;
},
parseMesh: function parseMesh(geometry, layer) {
var mesh;
var materials = this.getMaterials(geometry.userData.matNames, layer.geometry.type);
this.duplicateUVs(geometry, materials);
if (layer.geometry.type === 'points') mesh = new Points(geometry, materials);else if (layer.geometry.type === 'lines') mesh = new LineSegments(geometry, materials);else mesh = new Mesh(geometry, materials);
if (layer.name) mesh.name = layer.name;else mesh.name = this.defaultLayerName + '_layer_' + layer.number;
mesh.userData.pivot = layer.pivot;
return mesh;
},
// TODO: may need to be reversed in z to convert LWO to three.js coordinates
applyPivots: function applyPivots(meshes) {
meshes.forEach(function (mesh) {
mesh.traverse(function (child) {
var pivot = child.userData.pivot;
child.position.x += pivot[0];
child.position.y += pivot[1];
child.position.z += pivot[2];
if (child.parent) {
var parentPivot = child.parent.userData.pivot;
child.position.x -= parentPivot[0];
child.position.y -= parentPivot[1];
child.position.z -= parentPivot[2];
}
});
});
},
getMaterials: function getMaterials(namesArray, type) {
var materials = [];
var scope = this;
namesArray.forEach(function (name, i) {
materials[i] = scope.getMaterialByName(name);
}); // convert materials to line or point mats if required
if (type === 'points' || type === 'lines') {
materials.forEach(function (mat, i) {
var spec = {
color: mat.color
};
if (type === 'points') {
spec.size = 0.1;
spec.map = mat.map;
spec.morphTargets = mat.morphTargets;
materials[i] = new PointsMaterial(spec);
} else if (type === 'lines') {
materials[i] = new LineBasicMaterial(spec);
}
});
} // if there is only one material, return that directly instead of array
var filtered = materials.filter(Boolean);
if (filtered.length === 1) return filtered[0];
return materials;
},
getMaterialByName: function getMaterialByName(name) {
return this.materials.filter(function (m) {
return m.name === name;
})[0];
},
// If the material has an aoMap, duplicate UVs
duplicateUVs: function duplicateUVs(geometry, materials) {
var duplicateUVs = false;
if (!_Array$isArray(materials)) {
if (materials.aoMap) duplicateUVs = true;
} else {
materials.forEach(function (material) {
if (material.aoMap) duplicateUVs = true;
});
}
if (!duplicateUVs) return;
geometry.setAttribute('uv2', new BufferAttribute(geometry.attributes.uv.array, 2));
}
};
function MaterialParser(textureLoader) {
this.textureLoader = textureLoader;
}
MaterialParser.prototype = {
constructor: MaterialParser,
parse: function parse() {
var materials = [];
this.textures = {};
for (var name in lwoTree.materials) {
if (lwoTree.format === 'LWO3') {
materials.push(this.parseMaterial(lwoTree.materials[name], name, lwoTree.textures));
} else if (lwoTree.format === 'LWO2') {
materials.push(this.parseMaterialLwo2(lwoTree.materials[name], name, lwoTree.textures));
}
}
return materials;
},
parseMaterial: function parseMaterial(materialData, name, textures) {
var params = {
name: name,
side: this.getSide(materialData.attributes),
flatShading: this.getSmooth(materialData.attributes)
};
var connections = this.parseConnections(materialData.connections, materialData.nodes);
var maps = this.parseTextureNodes(connections.maps);
this.parseAttributeImageMaps(connections.attributes, textures, maps, materialData.maps);
var attributes = this.parseAttributes(connections.attributes, maps);
this.parseEnvMap(connections, maps, attributes);
params = _Object$assign(maps, params);
params = _Object$assign(params, attributes);
var materialType = this.getMaterialType(connections.attributes);
return new materialType(params);
},
parseMaterialLwo2: function parseMaterialLwo2(materialData, name
/*, textures*/
) {
var params = {
name: name,
side: this.getSide(materialData.attributes),
flatShading: this.getSmooth(materialData.attributes)
};
var attributes = this.parseAttributes(materialData.attributes, {});
params = _Object$assign(params, attributes);
return new MeshPhongMaterial(params);
},
// Note: converting from left to right handed coords by switching x -> -x in vertices, and
// then switching mat FrontSide -> BackSide
// NB: this means that FrontSide and BackSide have been switched!
getSide: function getSide(attributes) {
if (!attributes.side) return BackSide;
switch (attributes.side) {
case 0:
case 1:
return BackSide;
case 2:
return FrontSide;
case 3:
return DoubleSide;
}
},
getSmooth: function getSmooth(attributes) {
if (!attributes.smooth) return true;
return !attributes.smooth;
},
parseConnections: function parseConnections(connections, nodes) {
var materialConnections = {
maps: {}
};
var inputName = connections.inputName;
var inputNodeName = connections.inputNodeName;
var nodeName = connections.nodeName;
var scope = this;
inputName.forEach(function (name, index) {
if (name === 'Material') {
var matNode = scope.getNodeByRefName(inputNodeName[index], nodes);
materialConnections.attributes = matNode.attributes;
materialConnections.envMap = matNode.fileName;
materialConnections.name = inputNodeName[index];
}
});
nodeName.forEach(function (name, index) {
if (name === materialConnections.name) {
materialConnections.maps[inputName[index]] = scope.getNodeByRefName(inputNodeName[index], nodes);
}
});
return materialConnections;
},
getNodeByRefName: function getNodeByRefName(refName, nodes) {
for (var name in nodes) {
if (nodes[name].refName === refName) return nodes[name];
}
},
parseTextureNodes: function parseTextureNodes(textureNodes) {
var maps = {};
for (var name in textureNodes) {
var node = textureNodes[name];
var path = node.fileName;
if (!path) return;
var texture = this.loadTexture(path);
if (node.widthWrappingMode !== undefined) texture.wrapS = this.getWrappingType(node.widthWrappingMode);
if (node.heightWrappingMode !== undefined) texture.wrapT = this.getWrappingType(node.heightWrappingMode);
switch (name) {
case 'Color':
maps.map = texture;
break;
case 'Roughness':
maps.roughnessMap = texture;
maps.roughness = 0.5;
break;
case 'Specular':
maps.specularMap = texture;
maps.specular = 0xffffff;
break;
case 'Luminous':
maps.emissiveMap = texture;
maps.emissive = 0x808080;
break;
case 'Luminous Color':
maps.emissive = 0x808080;
break;
case 'Metallic':
maps.metalnessMap = texture;
maps.metalness = 0.5;
break;
case 'Transparency':
case 'Alpha':
maps.alphaMap = texture;
maps.transparent = true;
break;
case 'Normal':
maps.normalMap = texture;
if (node.amplitude !== undefined) maps.normalScale = new Vector2(node.amplitude, node.amplitude);
break;
case 'Bump':
maps.bumpMap = texture;
break;
}
} // LWO BSDF materials can have both spec and rough, but this is not valid in three
if (maps.roughnessMap && maps.specularMap) delete maps.specularMap;
return maps;
},
// maps can also be defined on individual material attributes, parse those here
// This occurs on Standard (Phong) surfaces
parseAttributeImageMaps: function parseAttributeImageMaps(attributes, textures, maps) {
for (var name in attributes) {
var attribute = attributes[name];
if (attribute.maps) {
var mapData = attribute.maps[0];
var path = this.getTexturePathByIndex(mapData.imageIndex, textures);
if (!path) return;
var texture = this.loadTexture(path);
if (mapData.wrap !== undefined) texture.wrapS = this.getWrappingType(mapData.wrap.w);
if (mapData.wrap !== undefined) texture.wrapT = this.getWrappingType(mapData.wrap.h);
switch (name) {
case 'Color':
maps.map = texture;
break;
case 'Diffuse':
maps.aoMap = texture;
break;
case 'Roughness':
maps.roughnessMap = texture;
maps.roughness = 1;
break;
case 'Specular':
maps.specularMap = texture;
maps.specular = 0xffffff;
break;
case 'Luminosity':
maps.emissiveMap = texture;
maps.emissive = 0x808080;
break;
case 'Metallic':
maps.metalnessMap = texture;
maps.metalness = 1;
break;
case 'Transparency':
case 'Alpha':
maps.alphaMap = texture;
maps.transparent = true;
break;
case 'Normal':
maps.normalMap = texture;
break;
case 'Bump':
maps.bumpMap = texture;
break;
}
}
}
},
parseAttributes: function parseAttributes(attributes, maps) {
var params = {}; // don't use color data if color map is present
if (attributes.Color && !maps.map) {
params.color = new Color().fromArray(attributes.Color.value);
} else params.color = new Color();
if (attributes.Transparency && attributes.Transparency.value !== 0) {
params.opacity = 1 - attributes.Transparency.value;
params.transparent = true;
}
if (attributes['Bump Height']) params.bumpScale = attributes['Bump Height'].value * 0.1;
if (attributes['Refraction Index']) params.refractionRatio = 1 / attributes['Refraction Index'].value;
this.parsePhysicalAttributes(params, attributes, maps);
this.parseStandardAttributes(params, attributes, maps);
this.parsePhongAttributes(params, attributes, maps);
return params;
},
parsePhysicalAttributes: function parsePhysicalAttributes(params, attributes
/*, maps*/
) {
if (attributes.Clearcoat && attributes.Clearcoat.value > 0) {
params.clearcoat = attributes.Clearcoat.value;
if (attributes['Clearcoat Gloss']) {
params.clearcoatRoughness = 0.5 * (1 - attributes['Clearcoat Gloss'].value);
}
}
},
parseStandardAttributes: function parseStandardAttributes(params, attributes, maps) {
if (attributes.Luminous) {
params.emissiveIntensity = attributes.Luminous.value;
if (attributes['Luminous Color'] && !maps.emissive) {
params.emissive = new Color().fromArray(attributes['Luminous Color'].value);
} else {
params.emissive = new Color(0x808080);
}
}
if (attributes.Roughness && !maps.roughnessMap) params.roughness = attributes.Roughness.value;
if (attributes.Metallic && !maps.metalnessMap) params.metalness = attributes.Metallic.value;
},
parsePhongAttributes: function parsePhongAttributes(params, attributes, maps) {
if (attributes.Diffuse) params.color.multiplyScalar(attributes.Diffuse.value);
if (attributes.Reflection) {
params.reflectivity = attributes.Reflection.value;
params.combine = AddOperation;
}
if (attributes.Luminosity) {
params.emissiveIntensity = attributes.Luminosity.value;
if (!maps.emissiveMap && !maps.map) {
params.emissive = params.color;
} else {
params.emissive = new Color(0x808080);
}
} // parse specular if there is no roughness - we will interpret the material as 'Phong' in this case
if (!attributes.Roughness && attributes.Specular && !maps.specularMap) {
if (attributes['Color Highlight']) {
params.specular = new Color().setScalar(attributes.Specular.value).lerp(params.color.clone().multiplyScalar(attributes.Specular.value), attributes['Color Highlight'].value);
} else {
params.specular = new Color().setScalar(attributes.Specular.value);
}
}
if (params.specular && attributes.Glossiness) params.shininess = 7 + Math.pow(2, attributes.Glossiness.value * 12 + 2);
},
parseEnvMap: function parseEnvMap(connections, maps, attributes) {
if (connections.envMap) {
var envMap = this.loadTexture(connections.envMap);
if (attributes.transparent && attributes.opacity < 0.999) {
envMap.mapping = EquirectangularRefractionMapping; // Reflectivity and refraction mapping don't work well together in Phong materials
if (attributes.reflectivity !== undefined) {
delete attributes.reflectivity;
delete attributes.combine;
}
if (attributes.metalness !== undefined) {
delete attributes.metalness;
}
} else envMap.mapping = EquirectangularReflectionMapping;
maps.envMap = envMap;
}
},
// get texture defined at top level by its index
getTexturePathByIndex: function getTexturePathByIndex(index) {
var fileName = '';
if (!lwoTree.textures) return fileName;
lwoTree.textures.forEach(function (texture) {
if (texture.index === index) fileName = texture.fileName;
});
return fileName;
},
loadTexture: function loadTexture(path) {
if (!path) return null;
var texture;
texture = this.textureLoader.load(path, undefined, undefined, function () {
console.warn('LWOLoader: non-standard resource hierarchy. Use \`resourcePath\` parameter to specify root content directory.');
});
return texture;
},
// 0 = Reset, 1 = Repeat, 2 = Mirror, 3 = Edge
getWrappingType: function getWrappingType(num) {
switch (num) {
case 0:
console.warn('LWOLoader: "Reset" texture wrapping type is not supported in three.js');
return ClampToEdgeWrapping;
case 1:
return RepeatWrapping;
case 2:
return MirroredRepeatWrapping;
case 3:
return ClampToEdgeWrapping;
}
},
getMaterialType: function getMaterialType(nodeData) {
if (nodeData.Clearcoat && nodeData.Clearcoat.value > 0) return MeshPhysicalMaterial;
if (nodeData.Roughness) return MeshStandardMaterial;
return MeshPhongMaterial;
}
};
function GeometryParser() {}
GeometryParser.prototype = {
constructor: GeometryParser,
parse: function parse(geoData, layer) {
var geometry = new BufferGeometry();
geometry.setAttribute('position', new Float32BufferAttribute(geoData.points, 3));
var indices = this.splitIndices(geoData.vertexIndices, geoData.polygonDimensions);
geometry.setIndex(indices);
this.parseGroups(geometry, geoData);
geometry.computeVertexNormals();
this.parseUVs(geometry, layer, indices);
this.parseMorphTargets(geometry, layer, indices); // TODO: z may need to be reversed to account for coordinate system change
geometry.translate(-layer.pivot[0], -layer.pivot[1], -layer.pivot[2]); // var userData = geometry.userData;
// geometry = geometry.toNonIndexed()
// geometry.userData = userData;
return geometry;
},
// split quads into tris
splitIndices: function splitIndices(indices, polygonDimensions) {
var remappedIndices = [];
var i = 0;
polygonDimensions.forEach(function (dim) {
if (dim < 4) {
for (var k = 0; k < dim; k++) {
remappedIndices.push(indices[i + k]);
}
} else if (dim === 4) {
remappedIndices.push(indices[i], indices[i + 1], indices[i + 2], indices[i], indices[i + 2], indices[i + 3]);
} else if (dim > 4) {
for (var k = 1; k < dim - 1; k++) {
remappedIndices.push(indices[i], indices[i + k], indices[i + k + 1]);
}
console.warn('LWOLoader: polygons with greater than 4 sides are not supported');
}
i += dim;
});
return remappedIndices;
},
// NOTE: currently ignoring poly indices and assuming that they are intelligently ordered
parseGroups: function parseGroups(geometry, geoData) {
var tags = lwoTree.tags;
var matNames = [];
var elemSize = 3;
if (geoData.type === 'lines') elemSize = 2;
if (geoData.type === 'points') elemSize = 1;
var remappedIndices = this.splitMaterialIndices(geoData.polygonDimensions, geoData.materialIndices);
var indexNum = 0; // create new indices in numerical order
var indexPairs = {}; // original indices mapped to numerical indices
var prevMaterialIndex;
var prevStart = 0;
var currentCount = 0;
for (var i = 0; i < remappedIndices.length; i += 2) {
var materialIndex = remappedIndices[i + 1];
if (i === 0) matNames[indexNum] = tags[materialIndex];
if (prevMaterialIndex === undefined) prevMaterialIndex = materialIndex;
if (materialIndex !== prevMaterialIndex) {
var currentIndex;
if (indexPairs[tags[prevMaterialIndex]]) {
currentIndex = indexPairs[tags[prevMaterialIndex]];
} else {
currentIndex = indexNum;
indexPairs[tags[prevMaterialIndex]] = indexNum;
matNames[indexNum] = tags[prevMaterialIndex];
indexNum++;
}
geometry.addGroup(prevStart, currentCount, currentIndex);
prevStart += currentCount;
prevMaterialIndex = materialIndex;
currentCount = 0;
}
currentCount += elemSize;
} // the loop above doesn't add the last group, do that here.
if (geometry.groups.length > 0) {
var currentIndex;
if (indexPairs[tags[materialIndex]]) {
currentIndex = indexPairs[tags[materialIndex]];
} else {
currentIndex = indexNum;
indexPairs[tags[materialIndex]] = indexNum;
matNames[indexNum] = tags[materialIndex];
}
geometry.addGroup(prevStart, currentCount, currentIndex);
} // Mat names from TAGS chunk, used to build up an array of materials for this geometry
geometry.userData.matNames = matNames;
},
splitMaterialIndices: function splitMaterialIndices(polygonDimensions, indices) {
var remappedIndices = [];
polygonDimensions.forEach(function (dim, i) {
if (dim <= 3) {
remappedIndices.push(indices[i * 2], indices[i * 2 + 1]);
} else if (dim === 4) {
remappedIndices.push(indices[i * 2], indices[i * 2 + 1], indices[i * 2], indices[i * 2 + 1]);
} else {
// ignore > 4 for now
for (var k = 0; k < dim - 2; k++) {
remappedIndices.push(indices[i * 2], indices[i * 2 + 1]);
}
}
});
return remappedIndices;
},
// UV maps:
// 1: are defined via index into an array of points, not into a geometry
// - the geometry is also defined by an index into this array, but the indexes may not match
// 2: there can be any number of UV maps for a single geometry. Here these are combined,
// with preference given to the first map encountered
// 3: UV maps can be partial - that is, defined for only a part of the geometry
// 4: UV maps can be VMAP or VMAD (discontinuous, to allow for seams). In practice, most
// UV maps are defined as partially VMAP and partially VMAD
// VMADs are currently not supported
parseUVs: function parseUVs(geometry, layer) {
// start by creating a UV map set to zero for the whole geometry
var remappedUVs = _Array$from(Array(geometry.attributes.position.count * 2), function () {
return 0;
});
for (var name in layer.uvs) {
var uvs = layer.uvs[name].uvs;
var uvIndices = layer.uvs[name].uvIndices;
uvIndices.forEach(function (i, j) {
remappedUVs[i * 2] = uvs[j * 2];
remappedUVs[i * 2 + 1] = uvs[j * 2 + 1];
});
}
geometry.setAttribute('uv', new Float32BufferAttribute(remappedUVs, 2));
},
parseMorphTargets: function parseMorphTargets(geometry, layer) {
var num = 0;
for (var name in layer.morphTargets) {
var remappedPoints = geometry.attributes.position.array.slice();
if (!geometry.morphAttributes.position) geometry.morphAttributes.position = [];
var morphPoints = layer.morphTargets[name].points;
var morphIndices = layer.morphTargets[name].indices;
var type = layer.morphTargets[name].type;
morphIndices.forEach(function (i, j) {
if (type === 'relative') {
remappedPoints[i * 3] += morphPoints[j * 3];
remappedPoints[i * 3 + 1] += morphPoints[j * 3 + 1];
remappedPoints[i * 3 + 2] += morphPoints[j * 3 + 2];
} else {
remappedPoints[i * 3] = morphPoints[j * 3];
remappedPoints[i * 3 + 1] = morphPoints[j * 3 + 1];
remappedPoints[i * 3 + 2] = morphPoints[j * 3 + 2];
}
});
geometry.morphAttributes.position[num] = new Float32BufferAttribute(remappedPoints, 3);
geometry.morphAttributes.position[num].name = name;
num++;
}
geometry.morphTargetsRelative = false;
}
}; // ************** UTILITY FUNCTIONS **************
function extractParentUrl(url, dir) {
var index = url.indexOf(dir);
if (index === -1) return './';
return url.substr(0, index);
}
export { LWOLoader };