@xtor/cga.js
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
361 lines (360 loc) • 13.6 kB
JavaScript
"use strict";
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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extrudeNext = exports.EndType = exports.JoinType = exports.isCCW = exports.extrude = exports.links = exports.linkSides = exports.linkSide = void 0;
var Vec3_1 = require("../math/Vec3");
var Vec2_1 = require("../math/Vec2");
var PolyLine_1 = require("../struct/3d/PolyLine");
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 recognition_1 = require("./recognition");
var Plane_1 = require("../struct/3d/Plane");
var threeaid_1 = require("../extends/threeaid");
/**
* 常用shape几何操作
*/
/**
* 缝合两个边
* @param {Array} side0
* @param {Array} side1
* @param {Boolean} isClosed
* @returns {Array<Vec3>} 三角形数组,每三个为一个三角形
*/
function linkSide(side0, side1, isClosed) {
if (isClosed === void 0) { isClosed = false; }
if (side0.length !== side1.length)
throw ("拉伸两边的点数量不一致 linkSide");
if (side0.length < 2 || side1.length < 2)
return [];
var sidelength = side0.length;
var orgLen = side0.length;
var length = isClosed ? 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(v10);
triangles.push(v11);
triangles.push(v00);
triangles.push(v11);
triangles.push(v01);
}
}
else {
if (side0[0].index !== undefined) {
//含索引的顶点
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(v10.index);
triangles.push(v11.index);
triangles.push(v00.index);
triangles.push(v11.index);
triangles.push(v01.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(v10);
triangles.push(v11);
triangles.push(v00);
triangles.push(v01);
triangles.push(v11);
}
}
}
return triangles;
}
exports.linkSide = linkSide;
/**
* 缝合shape集合
* @param {Array<Array<Point|Vec3>} shapes 路基 点集的集合, 每个shape的点数量一致
* @param {Boolean} isClosed 每一个shape是否是封闭的圈 默认false
* @returns {Array} 返回三角形集合 如果有所用范围索引,否则返回顶点
*/
function linkSides(shapes, isClosed, isClosed2) {
if (isClosed === void 0) { isClosed = false; }
if (isClosed2 === void 0) { isClosed2 = false; }
var length = isClosed2 ? shapes.length : shapes.length - 1;
var triangles = [];
for (var i = 0; i < length; i++) {
triangles.push.apply(triangles, linkSide(shapes[i], shapes[(i + 1) % shapes.length], isClosed));
}
return triangles;
}
exports.linkSides = linkSides;
/**
* 缝合集合
* @param sides 圈
* @param closed1 圈自身是否缝合
* @param closed2 圈拉伸后首尾是否缝合
*/
function links(sides, closed1, closed2) {
if (closed1 === void 0) { closed1 = false; }
if (closed2 === void 0) { closed2 = false; }
closed1 = sides[0] instanceof PolyLine_1.Polyline ? true : false || closed1;
return linkSides(sides, closed1, closed2);
}
exports.links = links;
var defaultExtrudeOption = {
sectionClosed: false,
pathClosed: false,
textureEnable: true,
textureScale: new Vec2_1.Vec2(1, 1),
smoothAngle: Math.PI / 180 * 30,
sealStart: false,
sealEnd: false,
normal: Vec3_1.Vec3.UnitZ,
vecdim: 3,
};
/**
* 挤压
* @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(shape, arg_path, options) {
if (options === void 0) { options = defaultExtrudeOption; }
options = __assign(__assign({}, defaultExtrudeOption), options);
if (arg_path.length < 2) {
throw ("路径节点数必须大于2");
}
var isCCW = recognition_1.recognitionCCW(shape);
if (!isCCW)
shape.reverse();
var normal = options.normal;
var startSeal = array_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[i].tangent.dot(shapepath[i + 1].tangent)) > options.smoothAngle)
shape.splice(i + insertNum++, 0, shapepath[i].clone());
}
if (options.isClosed) {
var dir1 = shapepath.get(-1).clone().sub(shapepath.get(-2)).normalize();
var dir2 = shapepath[0].clone().sub(shapepath.get(-1)).normalize();
if (Math.acos(dir1.dot(dir2)) > options.smoothAngle)
shape.push(shape.get(-1).clone());
//新加起始点纹理拉伸
shape.unshift(shape[0].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 = array_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 index = { index: 0 };
var vertices = array_1.flat(shapeArray);
mesh_1.indexable(vertices, index);
var triangles = linkSides(shapeArray, options.isClosed, options.isClosed2);
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[j].tlen * options.textureScale.x, path[i].tlen * options.textureScale.y);
}
}
var sealUv = array_1.clone(startSeal);
if (normal.dot(Vec3_1.Vec3.UnitZ) < 1 - 1e-4)
common_1.rotateByUnitVectors(sealUv, normal, Vec3_1.Vec3.UnitZ);
var endSeal = array_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, index);
if (options.sealEnd)
mesh_1.indexable(endSeal, index);
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);
triangles.push.apply(triangles, 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();
triangles.push.apply(triangles, sealEndTris);
for (var i = 0; i < sealUv.length; i++)
uvs.push(sealUv[i].x, sealUv[i].y);
}
return {
vertices: vertices,
triangles: triangles,
uvs: uvs
};
}
exports.extrude = extrude;
/**
* 是否逆时针
* counterclockwise
*/
function isCCW(shape) {
var d = 0;
for (var i = 0; i < shape.length; i++) {
var pt = shape[i];
var ptnext = shape[(i + 1) % shape.length];
d += -0.5 * (ptnext.y + pt.y) * (ptnext.x - pt.x);
}
return d > 0;
}
exports.isCCW = isCCW;
var JoinType;
(function (JoinType) {
JoinType[JoinType["Bevel"] = 0] = "Bevel";
JoinType[JoinType["Round"] = 1] = "Round";
JoinType[JoinType["Miter"] = 2] = "Miter";
})(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(shape, followPath, options) {
if (options === void 0) { options = defaultExtrudeOption; }
var shapeAry = [];
if (!isNaN(shape[0])) {
//数字数组转向量数据
var axis = ['x', 'y', 'z'];
for (var i = 0; i < shape.length; i += options.vecdim) {
var pt = new Vec3_1.Vec3();
for (var j = 0; j < options.vecdim; j++) {
pt[axis[j]] = shape[i + j];
}
shapeAry.push(pt);
}
shape = shapeAry;
}
//截面所在的平面
if (!recognition_1.recognitionCCW(shape)) {
//逆时针
shape.reverse();
}
if (!options.normal) {
//识别法线
options.normal = recognition_1.recognitionPlane(shape).normal;
}
//旋转到xy平面
if (options.center) {
//偏移
pointset_1.translate(shape, options.center);
}
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[i].tangent.dot(shapepath[i + 1].tangent)) > options.smoothAngle)
shape.splice(i + insertNum++, 0, shapepath[i].clone());
}
if (options.sealStart) {
}
if (options.sealEnd) {
}
//计算截面uv
for (var i = 0; i < shape.length; i++) {
var pt_1 = shape[i];
pt_1.u = pt_1.tlen;
var linkShapes = [];
for (var i_1 = 1; i_1 < followPath.length - 1; i_1++) {
var node = followPath[i_1];
var dir = node.tangent;
var newShape = array_1.clone(shape);
//节点平分线
var pnormal = followPath[i_1 + 1].clone().sub(followPath[i_1]).normalize().add(followPath[i_1].clone().sub(followPath[i_1 - 1]).normalize()).normalize();
var jointPlane = Plane_1.Plane.setFromPointNormal(node, pnormal);
jointPlane.negate();
var projectDir = Vec3_1.v3().subVecs(node, followPath[i_1 - 1]).normalize();
pointset_1.projectOnPlane(newShape, jointPlane, projectDir);
linkShapes.push(newShape);
}
threeaid_1.linksToGeometry(linkShapes);
}
}
exports.extrudeNext = extrudeNext;