xtorcga
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
655 lines (654 loc) • 26.5 kB
JavaScript
"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;