UNPKG

cgas

Version:

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

273 lines (231 loc) 8.83 kB
import { Vec3 } from '../math/Vec3'; import { Point } from '../struct/3d/Point'; import { Vec2 } from '../math/Vec2'; import { Polygon } from '../struct/3d/Polygon'; import { Polyline } from '../struct/3d/PolyLine'; import { Path } from '../struct/3d/Path'; import { rotateByUnitVectors } from './common'; import { translate } from '../utils/points'; import { indexable } from './mesh'; import { triangulation } from './trianglution'; import { clone, flat } from '../utils/array'; import { recognitionPlane, recognitionCCW } from './recognition'; /** * 常用shape几何操作 */ /** * 缝合两个边 * @param {Array} side0 * @param {Array} side1 * @param {Boolean} isClosed * @returns {Array<Vec3>} 三角形数组,每三个为一个三角形 */ export function linkSide(side0: Vec3[] | any, side1: Vec3[] | any, isClosed: boolean = 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; } /** * 缝合shape集合 * @param {Array<Array<Point|Vec3>} shapes 路基 点集的集合, 每个shape的点数量一致 * @param {Boolean} isClosed 每一个shape是否是封闭的圈 默认false * @returns {Array} 返回三角形集合 如果有所用范围索引,否则返回顶点 */ export function linkSides(shapes: Array<Array<Vec3 | Point>>, isClosed = false, isClosed2 = false) { var length = isClosed2 ? shapes.length : shapes.length - 1; var triangles = []; for (var i = 0; i < length; i++) { triangles.push(...linkSide(shapes[i], shapes[(i + 1) % shapes.length], isClosed)); } return triangles; } /** * 缝合集合 * @param sides 圈 * @param closed1 圈自身是否缝合 * @param closed2 圈拉伸后首尾是否缝合 */ export function links(sides: Array<Polygon | Polyline | Array<Vec3 | Point>>, closed1: boolean = false, closed2: boolean = false) { closed1 = sides[0] instanceof Polyline ? true : false || closed1; return linkSides(sides, closed1, closed2); } export interface IExtrudeOptions { isClosed?: boolean; isClosed2?: boolean; textureEnable?: boolean; textureScale?: Vec2; smoothAngle?: number; sealStart?: boolean; sealEnd?: boolean; normal?: Vec3, } const defaultExtrudeOption = { isClosed: false, isClosed2: false, textureEnable: true, textureScale: new Vec2(1, 1), smoothAngle: Math.PI / 180 * 30, sealStart: false, sealEnd: false, normal: Vec3.UnitZ, } /** * 挤压 * @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,是否密封结束面} */ export function extrude(shape: Polygon | Polyline | Array<Vec3>, arg_path: Array<Vec3> | any, options: IExtrudeOptions = defaultExtrudeOption) { options = { ...defaultExtrudeOption, ...options } if (arg_path.length < 2) { throw ("路径节点数必须大于2") } var isCCW = recognitionCCW(shape); if (!isCCW) shape.reverse(); var normal = options.normal; var startSeal = clone(shape); var shapepath = new Path(shape); var insertNum = 0; for (let 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((<any>shape).get(-1).clone()); //新加起始点纹理拉伸 shape.unshift(shape[0].clone()); } let path = arg_path; if (!(path instanceof Path) && path instanceof Array) path = new Path(arg_path); const shapeArray = []; for (let i = 0; i < path.length; i++) { const node = path[i]; var dir = node.tangent; var newShape = clone(shape); rotateByUnitVectors(newShape, normal!, dir); translate(newShape, node); shapeArray.push(newShape); } const index = { index: 0 }; var vertices = flat(shapeArray); indexable(vertices, index); var triangles = linkSides(shapeArray, options.isClosed, options.isClosed2); shapepath = new Path(shape); var uvs = []; for (let i = 0; i < path.length; i++) { for (let j = 0; j < shapepath.length; j++) { uvs.push(shapepath[j].tlen * options.textureScale!.x, path[i].tlen * options.textureScale!.y); } } var sealUv = clone(startSeal); if (normal!.dot(Vec3.UnitZ) < 1 - 1e-4) rotateByUnitVectors(sealUv, normal!, Vec3.UnitZ); var endSeal = clone(startSeal); rotateByUnitVectors(startSeal, normal!, path[0].tangent); translate(startSeal, path[0]) rotateByUnitVectors(endSeal, normal!, path.get(-1).tangent); translate(endSeal, path.get(-1)); var sealStartTris = triangulation(sealUv, [], { normal }); sealStartTris.reverse(); if (options.sealStart) indexable(startSeal, index); if (options.sealEnd) indexable(endSeal, index); var sealEndTris = [] var hasVLen = vertices.length; if (options.sealStart) for (let i = 0; i < sealStartTris.length; i++) { sealStartTris[i] += hasVLen; } if (options.sealEnd && !options.sealStart) for (let i = 0; i < sealStartTris.length; i++) { sealEndTris[i] = sealStartTris[i] + hasVLen; } if (options.sealEnd && options.sealStart) { for (let i = 0; i < sealStartTris.length; i++) { sealEndTris[i] = sealStartTris[i] + startSeal.length; } } if (options.sealStart) { vertices.push(...startSeal); triangles.push(...sealStartTris); for (let i = 0; i < sealUv.length; i++) uvs.push(sealUv[i].x, sealUv[i].y); } if (options.sealEnd) { vertices.push(...endSeal); sealEndTris.reverse(); triangles.push(...sealEndTris); for (let i = 0; i < sealUv.length; i++) uvs.push(sealUv[i].x, sealUv[i].y); } return { vertices, triangles, uvs }; }