UNPKG

xtorcga

Version:

Xtor Compute Geometry Algorithm Libary 计算几何算法库

655 lines (654 loc) 26.5 kB
"use strict"; /* * @Description : 挤压相关方法 * @Author : 赵耀圣 * @QQ : 549184003 * @Date : 2020-12-10 15:01:42 * @LastEditTime : 2021-09-14 10:07:25 * @FilePath : \cesium-taji-dabaod:\github\cga.js\src\alg\extrude.ts */ var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArrays = (this && this.__spreadArrays) || function () { for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; for (var r = Array(s), k = 0, i = 0; i < il; i++) for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) r[k] = a[j]; return r; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.extrudeNext = exports.EndType = exports.JoinType = exports.extrude_obsolete = exports.extrude = exports.linkSides = exports.linkSide = void 0; var Vec3_1 = require("../math/Vec3"); var Vec2_1 = require("../math/Vec2"); var Quat_1 = require("../math/Quat"); var Path_1 = require("../struct/3d/Path"); var common_1 = require("./common"); var pointset_1 = require("./pointset"); var mesh_1 = require("../render/mesh"); var trianglution_1 = require("./trianglution"); var array_1 = require("../utils/array"); var types_1 = require("../utils/types"); var Mat4_1 = require("../math/Mat4"); var Math_1 = require("../math/Math"); var vector_1 = require("../math/vector"); /** * 常用shape几何操作 */ /** * @description : 缝合两个边 不提供uv生成 uv有@linkSides 生成 * @param { ILinkSideOption } options * @returns { Array<Vec3>} 三角形数组,每三个为一个三角形 * @example : */ function linkSide(options) { options = __assign({ shapeClosed: true, autoUV: true }, options); var side0 = options.side0; var side1 = options.side1; var shapeClosed = options.shapeClosed; if (side0.length !== side1.length) throw ("拉伸两边的点数量不一致 linkSide"); if (side0.length < 2 || side1.length < 2) return []; var orgLen = side0.length; var length = shapeClosed ? side0.length : side0.length - 1; var triangles = []; if (side0[0] instanceof Number) { //索引三角形 for (var i = 0; i < length; i++) { var v00 = side0[i]; var v01 = side0[(i + 1) % orgLen]; var v10 = side1[i]; var v11 = side1[(i + 1) % orgLen]; triangles.push(v00); triangles.push(v01); triangles.push(v11); triangles.push(v00); triangles.push(v11); triangles.push(v10); } } else { if (types_1.isDefined(side0[0].index)) { //含索引的顶点 for (var i = 0; i < length; i++) { var v00 = side0[i]; var v01 = side0[(i + 1) % orgLen]; var v10 = side1[i]; var v11 = side1[(i + 1) % orgLen]; triangles.push(v00.index); triangles.push(v01.index); triangles.push(v11.index); triangles.push(v00.index); triangles.push(v11.index); triangles.push(v10.index); } } else { //三角形顶点 for (var i = 0; i < length; i++) { var v00 = side0[i]; var v01 = side0[(i + 1) % orgLen]; var v10 = side1[i]; var v11 = side1[(i + 1) % orgLen]; triangles.push(v00); triangles.push(v01); triangles.push(v11); triangles.push(v00); triangles.push(v11); triangles.push(v10); } } } if (options.holes0 && options.holes1) { var holes0 = options.holes0; var holes1 = options.holes1; for (var h = 0; h < holes0.length; h++) { var holeTriangles = linkSide({ side0: holes0[h], side1: holes1[h] }); holeTriangles.reverse(); triangles.push.apply(triangles, holeTriangles); } } return triangles; } exports.linkSide = linkSide; /** * @description : 链接多个shape 生成几何体 * @param {ILinkSideOptions} optionsILinkSideOptions { * shapes: Array<Array<IVec3 | number | any>>; * sealStart?: boolean,//开始封面 * sealEnd?: boolean;//结束封面 * shapeClosed?: boolean,//shape是否闭合 * pathClosed?: boolean,//路径是否闭合 * index?: { index: number }, * generateUV?: boolean * } * * @return {*} * @example : * */ function linkSides(options) { options = __assign({ sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false, generateUV: true, autoIndex: true, axisPlane: trianglution_1.AxisPlane.XY }, options); if (options.autoIndex) options.index = options.index || { index: 0 }; var shapes = options.shapes; var holess = options.holes; var hasHole = !!(holess && holess.length > 0); var length = options.pathClosed ? shapes.length : shapes.length - 1; var triangles = []; var index = options.index; var allVertics = [shapes]; if (hasHole) allVertics.push(holess); var orgShape = options.orgShape || shapes[0]; var orgHoles = options.orgHoles || (holess && holess[0]); if (index) mesh_1.indexable(allVertics, index); for (var i = 0; i < length; i++) { if (holess) triangles.push.apply(triangles, linkSide({ side0: shapes[i], side1: shapes[(i + 1) % shapes.length], holes0: holess[i], holes1: holess[(i + 1) % shapes.length], shapeClosed: options.shapeClosed })); else triangles.push.apply(triangles, linkSide({ side0: shapes[i], side1: shapes[(i + 1) % shapes.length], shapeClosed: options.shapeClosed })); } if (options.sealStart) { var startShape = common_1.clone(shapes[0]); allVertics.push(startShape); if (holess && holess[0]) { var startHoles = common_1.clone(holess[0]); allVertics.push(startHoles); } var startTris = trianglution_1.triangulation(orgShape, orgHoles, { feature: trianglution_1.AxisPlane.XYZ }); if (index) { startTris.forEach(function (v, i) { startTris[i] = v + (index === null || index === void 0 ? void 0 : index.index); }); index.index += startShape.length; if (holess && holess[0]) startHoles.forEach(function (h) { index.index += h.length; }); } triangles.push.apply(triangles, startTris.reverse()); } if (options.sealEnd) { var endShape = common_1.clone(shapes[shapes.length - 1]); allVertics.push(endShape); if (holess && holess[0]) { var endHoles = common_1.clone(common_1.clone(holess[holess.length - 1])); allVertics.push(endHoles); } var endTris = trianglution_1.triangulation(orgShape, orgHoles, { feature: trianglution_1.AxisPlane.XYZ }); if (index) { endTris.forEach(function (v, i) { endTris[i] = v + (index === null || index === void 0 ? void 0 : index.index); }); index.index += endShape.length; if (holess && holess[0]) endHoles.forEach(function (h) { index.index += h.length; }); } triangles.push.apply(triangles, endTris); } triangles.shapes = allVertics; var uvs = []; if (options.generateUV) { //生成UV // let uBasicScalar = new Array(shapes[0].length).fill(0); var uBasicScalar = 0; for (var i_1 = 0; i_1 < shapes.length; i_1++) { var shape = shapes[i_1]; var lastshape = shapes[i_1 - 1]; if (isNaN(shape[0])) { //不是索引才生产纹理,其他都是顶点 var vScalar = Path_1.Path.getPerMileages(shape, false); var uScalar = 0; // if (i > 0) // uScalar = uBasicScalar.map((e, k) => { // return e + shape[k].distanceTo(lastshape[k]); // }); // else // uScalar = new Array(shapes[0].length).fill(0); if (i_1 > 0) uScalar = uBasicScalar + shape[0].distanceTo(lastshape[0]); for (var l = 0; l < shape.length; l++) { uvs.push(uScalar, vScalar[l]); } uBasicScalar = uScalar; } else console.error("索引无法生成纹理"); } if (holess) { uBasicScalar = 0; for (var i_2 = 0; i_2 < holess.length; i_2++) { var holes = holess[i_2]; var lastHole = holess[i_2 - 1]; var uScalar = 0; if (i_2 > 0) uScalar = uBasicScalar + holes[0][0].distanceTo(lastHole[0][0]); for (var j = 0; j < holes.length; j++) { var hole = holes[j]; var vScalar = Path_1.Path.getPerMileages(hole, false); for (var l = 0; l < hole.length; l++) { uvs.push(uScalar, vScalar[l]); } } uBasicScalar = uScalar; } } //前后纹理 var sealUvs = []; switch (options.axisPlane) { case trianglution_1.AxisPlane.XY: orgShape.map(function (e) { sealUvs.push(e.x, e.y); }); if (orgHoles) orgHoles.forEach(function (h) { h.forEach(function (e) { sealUvs.push(e.x, e.y); }); }); break; case trianglution_1.AxisPlane.XZ: orgShape.map(function (e) { sealUvs.push(e.x, e.z); }); if (orgHoles) orgHoles.forEach(function (h) { h.forEach(function (e) { sealUvs.push(e.x, e.z); }); }); break; case trianglution_1.AxisPlane.YZ: orgShape.map(function (e) { sealUvs.push(e.y, e.z); }); if (orgHoles) orgHoles.forEach(function (h) { h.forEach(function (e) { sealUvs.push(e.y, e.z); }); }); break; default: break; } uvs.push.apply(uvs, __spreadArrays(sealUvs, sealUvs)); } var indices = triangles || []; // if (isDefined(shapes[0][0].index)) { // //收集索引 // for (let i = 0; i < shapes.length; i++) { // const shape = shapes[i]; // for (let j = 0; j < shape.length; j++) { // const v = shape[j]; // indices.push(v.index); // } // } // } var positions = common_1.verctorToNumbers(allVertics); shapes.pop(); shapes.pop(); return { position: positions, index: indices, uv: uvs }; } exports.linkSides = linkSides; var defaultExtrudeOption = { textureEnable: true, textureScale: new Vec2_1.Vec2(1, 1), smoothAngle: Math.PI / 180 * 30, sealStart: false, sealEnd: false, normal: Vec3_1.Vec3.UnitZ, }; var _matrix = Mat4_1.m4(); var _matrix1 = Mat4_1.m4(); var _quat = Quat_1.quat(); var _quat1 = Quat_1.quat(); var _vec1 = Vec3_1.v3(); /** * @description : 挤压形状生成几何体 * @param {IExtrudeOptionsEx} options * IExtrudeOptionsEx { * shape: Array<Vec3 | IVec3 | Vec2 | IVec2>;//shape默认的矩阵为正交矩阵 * path: Array<Vec3 | IVec3>;//挤压路径 * ups?: Array<Vec3 | IVec3>; * up?: Vec3 | IVec3; * shapeClosed?: boolean;//闭合为多边形 界面 * pathClosed?: boolean;//首尾闭合为圈 * textureEnable?: boolean; * smoothAngle?: number; * sealStart?: boolean; * sealEnd?: boolean; * normal?: Vec3,//面的法线 * autoIndex?: boolean, * index?: { index: number } * holes?: Array<Vec3 | IVec3 | Vec2 | IVec2>[] *} * @return {IGeometry} * @example : * */ function extrude(options) { options = __assign({ sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false, generateUV: true, autoIndex: true, axisPlane: trianglution_1.AxisPlane.XY, up: Vec3_1.Vec3.Up, smoothAngle: 30 * Math_1.RADIANS_PER_DEGREE, enableSmooth: false }, options); if (!vector_1.vector.isCCW(options.shape)) options.shape.reverse(); if (options.holes) options.holes.forEach(function (hole) { if (!vector_1.vector.isCCW(options.shape)) hole.reverse(); }); var path = new Path_1.Path(options.path); var shapes = []; var shape = options.shape; if (options.shapeClosed && !shape[0].equals(shape[shape.length - 1])) shape.push(shape[0].clone()); var shapePath = new Path_1.Path(shape, options.shapeClosed); if (options.enableSmooth) for (var i = 1; i < shapePath.length; i++) { //大角度插入点 角度过大为了呈现flat shader的效果 if (shapePath.get(i).direction.dot(shapePath.get((i + 1) % shapePath.length).direction) < Math.cos(options.smoothAngle)) { shapePath.splice(i + 1, 0, shapePath.get(i).clone()); i++; } } var ups = options.ups || []; if (types_1.isUndefined(shapePath.first.z)) { shapePath.array = shapePath.array.map(function (e) { return Vec3_1.v3(e.x, e.y, 0); }); options.normal = options.normal || Vec3_1.Vec3.UnitZ; } var up = options.up; var right = options.right; var newholes = []; for (var i = 0; i < options.path.length; i++) { var point = path.get(i); var direction = point.direction; var upi = void 0; upi = ups[i] || up || Vec3_1.v3().crossVecs(right, direction); var righti = right; if (!right) righti = Vec3_1.v3().crossVecs(upi, direction).normalize(); _matrix.makeBasis(righti, upi, direction); _matrix.setPosition(point); var new_shape = shapePath.clone(); new_shape.applyMat4(_matrix); shapes.push(new_shape); if (options.holes) { var mholes = pointset_1.applyMat4(options.holes, _matrix, false); newholes.push(mholes); } } var geo = linkSides({ shapes: shapes.map(function (e) { return e._array; }), holes: newholes, orgShape: shapePath._array, orgHoles: options.holes, sealStart: options.sealStart, sealEnd: options.sealEnd, shapeClosed: options.shapeClosed, pathClosed: options.pathClosed, axisPlane: options.axisPlane, autoIndex: options.autoIndex, generateUV: options.generateUV, }); return geo; } exports.extrude = extrude; /** * 挤压 * @param {Polygon|Array<Point|Vec3> } shape 多边形或顶点数组 * @param {Path|Array<Point|Vec3> } path 路径或者或顶点数组 * @param {Object} options { * isClosed: false,闭合为多边形 界面 * isClosed2: false, 闭合为圈 * textureEnable: true, 计算纹理坐标 * textureScale: new Vec2(1, 1),纹理坐标缩放 * smoothAngle: Math.PI / 180 * 30,大于这个角度则不平滑 * sealStart: true, 是否密封开始面 * sealEnd: true,是否密封结束面} */ function extrude_obsolete(shape, arg_path, options) { var _a, _b; if (options === void 0) { options = defaultExtrudeOption; } options = __assign(__assign({}, defaultExtrudeOption), options); if (arg_path.length < 2) { throw ("路径节点数必须大于2"); } var isCCW = vector_1.vector.isCCW(shape); if (!isCCW) shape.reverse(); var normal = options.normal; var startSeal = common_1.clone(shape); var shapepath = new Path_1.Path(shape); var insertNum = 0; for (var i = 1; i < shapepath.length - 1; i++) { //大角度插入点 角度过大为了呈现flat shader的效果 if (Math.acos(shapepath.get(i).tangent.dot(shapepath.get(i + 1).tangent)) > options.smoothAngle) shape.splice(i + insertNum++, 0, shapepath.get(i).clone()); } if (options.shapeClosed) { var dir1 = shapepath.get(-1).clone().sub(shapepath.get(-2)).normalize(); var dir2 = shapepath.get(0).clone().sub(shapepath.get(-1)).normalize(); if (Math.acos(dir1.dot(dir2)) > options.smoothAngle) shape.push(shape.get(-1).clone()); //新加起始点纹理拉伸 shape.unshift(shape.first.clone()); } var path = arg_path; if (!(path instanceof Path_1.Path) && path instanceof Array) path = new Path_1.Path(arg_path); var shapeArray = []; for (var i = 0; i < path.length; i++) { var node = path[i]; var dir = node.tangent; var newShape = common_1.clone(shape); common_1.rotateByUnitVectors(newShape, normal, dir); if (options.fixedY) { var v = Vec3_1.Vec3.UnitX; common_1.rotateByUnitVectors([v], normal, dir); var v1 = v.clone(); v1.y = 0; common_1.rotateByUnitVectors(newShape, v, v1); } pointset_1.translate(newShape, node); shapeArray.push(newShape); } var gindex = { index: 0 }; var vertices = array_1.flat(shapeArray); mesh_1.indexable(vertices, gindex); var index = linkSides({ shapes: shapeArray, shapeClosed: options.shapeClosed, pathClosed: options.isClosed2, orgShape: shape }).index; shapepath = new Path_1.Path(shape); var uvs = []; for (var i = 0; i < path.length; i++) { for (var j = 0; j < shapepath.length; j++) { uvs.push(shapepath.get(j).tlen * options.textureScale.x, path.get(i).tlen * options.textureScale.y); } } var sealUv = common_1.clone(startSeal); if (normal.dot(Vec3_1.Vec3.UnitZ) < 1 - 1e-4) common_1.rotateByUnitVectors(sealUv, normal, Vec3_1.Vec3.UnitZ); var endSeal = common_1.clone(startSeal); common_1.rotateByUnitVectors(startSeal, normal, path[0].tangent); if (options.fixedY) { var v = Vec3_1.Vec3.UnitX; common_1.rotateByUnitVectors([v], normal, path[0].tangent); var v1 = v.clone(); v1.y = 0; common_1.rotateByUnitVectors(startSeal, v, v1); } pointset_1.translate(startSeal, path[0]); common_1.rotateByUnitVectors(endSeal, normal, path.get(-1).tangent); if (options.fixedY) { var v = Vec3_1.Vec3.UnitX; common_1.rotateByUnitVectors([v], normal, path.get(-1).tangent); var v1 = v.clone(); v1.y = 0; common_1.rotateByUnitVectors(endSeal, v, v1); } pointset_1.translate(endSeal, path.get(-1)); var sealStartTris = trianglution_1.triangulation(sealUv, [], { normal: normal }); sealStartTris.reverse(); if (options.sealStart) mesh_1.indexable(startSeal, gindex); if (options.sealEnd) mesh_1.indexable(endSeal, gindex); var sealEndTris = []; var hasVLen = vertices.length; if (options.sealStart) for (var i = 0; i < sealStartTris.length; i++) { sealStartTris[i] += hasVLen; } if (options.sealEnd && !options.sealStart) for (var i = 0; i < sealStartTris.length; i++) { sealEndTris[i] = sealStartTris[i] + hasVLen; } if (options.sealEnd && options.sealStart) { for (var i = 0; i < sealStartTris.length; i++) { sealEndTris[i] = sealStartTris[i] + startSeal.length; } } if (options.sealStart) { vertices.push.apply(vertices, startSeal); (_a = index).push.apply(_a, sealStartTris); for (var i = 0; i < sealUv.length; i++) uvs.push(sealUv[i].x, sealUv[i].y); } if (options.sealEnd) { vertices.push.apply(vertices, endSeal); sealEndTris.reverse(); (_b = index).push.apply(_b, sealEndTris); for (var i = 0; i < sealUv.length; i++) uvs.push(sealUv[i].x, sealUv[i].y); } return { vertices: vertices, index: index, uvs: uvs }; } exports.extrude_obsolete = extrude_obsolete; var JoinType; (function (JoinType) { JoinType[JoinType["Square"] = 0] = "Square"; JoinType[JoinType["Round"] = 1] = "Round"; JoinType[JoinType["Miter"] = 2] = "Miter"; JoinType[JoinType["Bevel"] = 0] = "Bevel"; })(JoinType = exports.JoinType || (exports.JoinType = {})); var EndType; (function (EndType) { EndType[EndType["Square"] = 0] = "Square"; EndType[EndType["Round"] = 1] = "Round"; EndType[EndType["Butt"] = 2] = "Butt"; })(EndType = exports.EndType || (exports.EndType = {})); /** * 将路径看做挤压操作中心 * * @param shape * @param followPath * @param options */ function extrudeNext(options) { options = __assign({ sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false, generateUV: true, autoIndex: true, axisPlane: trianglution_1.AxisPlane.XY, smoothAngle: 30 * Math_1.RADIANS_PER_DEGREE, enableSmooth: false }, options); var path = options.shapeCenter ? pointset_1.translate(options.path, options.shapeCenter, false) : options.path; var shape = options.shape; array_1.unique(path, function (a, b) { return a.equals(b); }); array_1.unique(shape, function (a, b) { return a.equals(b); }); var pathPath = new Path_1.Path(path, options.pathClosed, true); var starti = options.shapeClosed ? 0 : 1; var shapePath = new Path_1.Path(shape, options.shapeClosed); if (options.enableSmooth) for (var i = 1; i < shapePath.length; i++) { //大角度插入点 角度过大为了呈现flat shader的效果 if (shapePath.get(i).direction.dot(shapePath.get((i + 1) % shapePath.length).direction) < options.smoothAngle) { shapePath.splice(i + 1, 0, shapePath.get(i).clone()); i++; } } options.normal = options.normal || Vec3_1.Vec3.UnitZ; if (types_1.isUndefined(shapePath.first.z)) { shapePath.array = shapePath.array.map(function (e) { return Vec3_1.v3(e.x, e.y, 0); }); } var up = options.up; var right = options.right; var shapes = [], newholes = []; var accMat = Mat4_1.m4(); /** * 如果路径闭合 要考虑首尾shape矩阵变化后还能一致吻合 */ switch (options.jtType) { case JoinType.Square: //切角 break; case JoinType.Round: //圆角 break; case JoinType.Miter: //直角 for (var i = 0; i < pathPath.length; i++) { var p = pathPath.get(i); var dir = p.direction; var bdir = p.bdirection; var bnormal = p.bnormal; var normal = p.normal; //相邻两个向量发生的旋转 if (i === 0) { _quat.setFromUnitVecs(Vec3_1.Vec3.UnitZ, dir); } else { _quat.setFromUnitVecs(pathPath.get(i - 1).direction, dir); } var new_shape = shapePath.clone(); //旋转 _matrix.makeRotationFromQuat(_quat); accMat.premultiply(_matrix); _quat.setFromUnitVecs(dir, bdir); _matrix.makeRotationFromQuat(_quat); _matrix.multiply(accMat); // /旋转到原地缩放----开始----------------------- var cosA = dir.dot(bdir); var shear = 1 / cosA; _vec1.crossVecs(normal, bdir); _matrix1.copy(_matrix); _matrix1.invert(); _vec1.applyMat4(_matrix1); _quat.setFromUnitVecs(_vec1, Vec3_1.Vec3.Up); _matrix1.makeRotationFromQuat(_quat); new_shape.applyMat4(_matrix1); new_shape.scale(1, shear, 1); new_shape.applyMat4(_matrix1.invert()); // /旋转到原地缩放----结束----------------------- //位置 _matrix.setPosition(p); new_shape.applyMat4(_matrix); // if (options.holes) { // const mholes = applyMat4(options.holes, _matrix1, false); // applyMat4(mholes, _matrix, true); // newholes.push(mholes); // } shapes.push(new_shape); } break; default: break; } var geo = linkSides({ shapes: shapes.map(function (e) { return e.array; }), holes: newholes, orgShape: shapePath._array, orgHoles: options.holes, sealStart: options.sealStart, sealEnd: options.sealEnd, shapeClosed: options.shapeClosed, pathClosed: options.pathClosed, axisPlane: options.axisPlane, autoIndex: options.autoIndex, generateUV: options.generateUV, }); return geo; } exports.extrudeNext = extrudeNext;