cgas
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
273 lines (231 loc) • 8.83 kB
text/typescript
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
};
}