xtorcga
Version:
Xtor Compute Geometry Algorithm Libary 计算几何算法库
857 lines (735 loc) • 28.3 kB
text/typescript
/*
* @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
*/
import { Vec3, v3, IVec3, IVec2 } from '../math/Vec3';
import { Vec2 } from '../math/Vec2';
import { quat } from '../math/Quat';
import { Polygon } from '../struct/3d/Polygon';
import { Polyline } from '../struct/3d/Polyline';
import { Path } from '../struct/3d/Path';
import { clone, rotateByUnitVectors, verctorToNumbers, angle, applyQuat, scale } from './common';
import { applyMat4, projectOnPlane, translate } from './pointset';
import { indexable } from '../render/mesh';
import { AxisPlane, triangulation } from './trianglution';
import { flat, unique } from '../utils/array';
import { isDefined, isUndefined } from '../utils/types';
import { m4 } from '../math/Mat4';
import { IGeometry } from '../render/geometry';
import { RADIANS_PER_DEGREE } from '../math/Math';
import { vector } from '../math/vector';
import { ArrayList } from '../struct/data/ArrayList';
import { Plane } from '../struct/3d/Plane';
export interface ILinkSideOption {
side0: { x: number, y: number, z: number, index?: number }[] | number[];//可能是点 也可能是索引
side1: { x: number, y: number, z: number, index?: number }[] | number[];
holes0?: Array<Array<Vec3>>;
holes1?: Array<Array<Vec3>>;
shapeClosed?: boolean;
autoUV?: boolean;
uvScalars?: number[],
segs?: []
}
/**
* 常用shape几何操作
*/
/**
* @description : 缝合两个边 不提供uv生成 uv有@linkSides 生成
* @param { ILinkSideOption } options
* @returns { Array<Vec3>} 三角形数组,每三个为一个三角形
* @example :
*/
export function linkSide(options: ILinkSideOption) {
options = { shapeClosed: true, autoUV: true, ...options };
const side0: any = options.side0;
const side1: any = options.side1;
const 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: number[] = [];
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 (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) {
const holes0 = options.holes0;
const holes1 = options.holes1;
for (let h = 0; h < holes0.length; h++) {
const holeTriangles: any = linkSide({ side0: holes0[h], side1: holes1[h] })
holeTriangles.reverse();
triangles.push(...holeTriangles);
}
}
return triangles;
}
/**
* 缝合shape集合
* @param {Array<Array<Point|Vec3>} shapes 路基 点集的集合, 每个shape的点数量一致
* @param {Boolean} sealStart 每一个shape是否是封闭的界面 默认false
* @param {Boolean} isClosed 每一个shape是否是封闭的界面 默认false
* @param {Boolean} isClosed2 每一个shape是否是封闭的首尾 默认false
* @returns {Array} 返回三角形集合 如果有所用范围索引,否则返回顶点
*/
export interface ILinkSideOptions {
shapes: Array<Array<IVec3 | any | IVec3>>;
orgShape?: Array<IVec3 | any | IVec3>;
orgHoles?: any;
sealStart?: boolean,//开始封面
sealEnd?: boolean;//结束封面
shapeClosed?: boolean,//shape是否闭合
pathClosed?: boolean,//路径是否闭合
index?: { index: number },
autoIndex?: boolean
generateUV?: boolean
axisPlane?: AxisPlane
holes?: Array<Array<IVec3 | any | IVec3>>[]
}
/**
* @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 :
*
*/
export function linkSides(options: ILinkSideOptions): IGeometry {
options = {
sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false,
generateUV: true,
autoIndex: true,
axisPlane: AxisPlane.XY,
...options
}
if (options.autoIndex)
options.index = options.index || { index: 0 };
const shapes = options.shapes;
const holess = options.holes;
const hasHole: boolean = !!(holess && holess.length > 0);
var length = options.pathClosed ? shapes.length : shapes.length - 1;
var triangles: any = [];
const index = options.index;
var allVertics = [shapes];
if (hasHole)
allVertics.push(holess as any);
var orgShape = options.orgShape || shapes[0];
var orgHoles = options.orgHoles || (holess && holess[0]);
if (index)
indexable(allVertics, index);
for (var i = 0; i < length; i++) {
if (holess)
triangles.push(...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(...linkSide({ side0: shapes[i], side1: shapes[(i + 1) % shapes.length], shapeClosed: options.shapeClosed }));
}
if (options.sealStart) {
const startShape = clone(shapes[0]);
allVertics.push(startShape);
if (holess && holess[0]) {
var startHoles = clone(holess[0])
allVertics.push(startHoles)
}
var startTris = triangulation(orgShape, orgHoles, { feature: AxisPlane.XYZ });
if (index) {
startTris.forEach((v: number, i: number) => {
startTris[i] = v + index?.index;
})
index.index += startShape.length
if (holess && holess[0])
startHoles.forEach((h: any) => {
index.index += h.length
})
}
triangles.push(...startTris.reverse());
}
if (options.sealEnd) {
const endShape = clone(shapes[shapes.length - 1]);
allVertics.push(endShape);
if (holess && holess[0]) {
var endHoles = clone(clone(holess[holess.length - 1]));
allVertics.push(endHoles)
}
var endTris = triangulation(orgShape, orgHoles, { feature: AxisPlane.XYZ });
if (index) {
endTris.forEach((v: number, i: number) => {
endTris[i] = v + index?.index;
})
index.index += endShape.length
if (holess && holess[0])
endHoles.forEach((h: any) => {
index.index += h.length
})
}
triangles.push(...endTris);
}
triangles.shapes = allVertics;
var uvs = []
if (options.generateUV) {
//生成UV
// let uBasicScalar = new Array(shapes[0].length).fill(0);
let uBasicScalar = 0;
for (let i = 0; i < shapes.length; i++) {
const shape: Vec3[] = shapes[i] as unknown as Vec3[];
const lastshape: Vec3[] = shapes[i - 1] as unknown as Vec3[];
if (isNaN(shape[0] as any)) {
//不是索引才生产纹理,其他都是顶点
var vScalar = 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 > 0)
uScalar = uBasicScalar + shape[0].distanceTo(lastshape[0]);
for (let l = 0; l < shape.length; l++) {
uvs.push(uScalar, vScalar[l]);
}
uBasicScalar = uScalar;
}
else
console.error("索引无法生成纹理")
}
if (holess) {
uBasicScalar = 0;
for (let i = 0; i < holess.length; i++) {
const holes = holess[i];
const lastHole: any = holess[i - 1];
var uScalar = 0;
if (i > 0)
uScalar = uBasicScalar + holes[0][0].distanceTo(lastHole[0][0]);
for (let j = 0; j < holes.length; j++) {
const hole: any = holes[j];
var vScalar = Path.getPerMileages(hole, false);
for (let l = 0; l < hole.length; l++) {
uvs.push(uScalar, vScalar[l]);
}
}
uBasicScalar = uScalar;
}
}
//前后纹理
var sealUvs: any = []
switch (options.axisPlane) {
case AxisPlane.XY:
orgShape.map(e => {
sealUvs.push(e.x, e.y)
})
if (orgHoles)
orgHoles.forEach((h: any) => {
h.forEach((e: any) => {
sealUvs.push(e.x, e.y)
})
})
break;
case AxisPlane.XZ:
orgShape.map(e => {
sealUvs.push(e.x, e.z)
})
if (orgHoles)
orgHoles.forEach((h: any) => {
h.forEach((e: any) => {
sealUvs.push(e.x, e.z)
})
})
break;
case AxisPlane.YZ:
orgShape.map(e => {
sealUvs.push(e.y, e.z)
})
if (orgHoles)
orgHoles.forEach((h: any) => {
h.forEach((e: any) => {
sealUvs.push(e.y, e.z)
})
})
break;
default:
break;
}
uvs.push(...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);
// }
// }
// }
const positions = verctorToNumbers(allVertics);
shapes.pop();
shapes.pop();
return { position: positions, index: indices, uv: uvs };
}
export interface IExtrudeOptions {
fixedY?: boolean;
shapeClosed?: boolean;//闭合为多边形 界面
isClosed2?: boolean;//首尾闭合为圈
textureEnable?: boolean;
textureScale?: Vec2;
smoothAngle?: number;
sealStart?: boolean;
sealEnd?: boolean;
normal?: Vec3,
}
const defaultExtrudeOption: IExtrudeOptions = {
textureEnable: true,
textureScale: new Vec2(1, 1),
smoothAngle: Math.PI / 180 * 30,
sealStart: false,
sealEnd: false,
normal: Vec3.UnitZ,
}
export interface IExtrudeOptionsEx {
shape: Array<Vec3 | IVec3 | Vec2 | IVec2>;//shape默认的矩阵为正交矩阵
path: Array<Vec3 | IVec3>;
ups?: Array<Vec3 | IVec3>;
up?: Vec3 | IVec3;
right?: Vec3;
shapeClosed?: boolean;//闭合为多边形 界面
pathClosed?: boolean;//首尾闭合为圈
textureEnable?: boolean;
smoothAngle?: number;
enableSmooth?: boolean;
sealStart?: boolean;
sealEnd?: boolean;
normal?: Vec3,
autoIndex?: boolean,
axisPlane?: AxisPlane,
generateUV?: boolean,
index?: { index: number },
holes?: Array<Vec3 | IVec3 | Vec2 | IVec2>[]
}
const _matrix = m4();
const _matrix1 = m4();
const _quat = quat();
const _quat1 = quat();
const _vec1 = 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 :
*
*/
export function extrude(options: IExtrudeOptionsEx): IGeometry {
options = {
sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false,
generateUV: true,
autoIndex: true,
axisPlane: AxisPlane.XY,
up: Vec3.Up,
smoothAngle: 30 * RADIANS_PER_DEGREE,
enableSmooth: false,
...options
}
if (!vector.isCCW(options.shape))
options.shape.reverse();
if (options.holes)
options.holes.forEach((hole) => {
if (!vector.isCCW(options.shape))
hole.reverse();
})
const path = new Path(options.path as any);
const shapes = [];
let shape: any = options.shape;
if (options.shapeClosed && !shape[0].equals(shape[shape.length - 1]))
shape.push(shape[0].clone())
let shapePath: Path<Vec3> | any = new Path(shape, options.shapeClosed);
if (options.enableSmooth)
for (let 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++;
}
}
const ups = options.ups || [];
if (isUndefined(shapePath.first.z)) {
shapePath.array = shapePath.array.map((e: any) => v3(e.x, e.y, 0));
options.normal = options.normal || Vec3.UnitZ;
}
var up: Vec3 = options.up as Vec3;
var right: Vec3 = options.right as Vec3;
var newholes = [];
for (let i = 0; i < options.path.length; i++) {
const point = path.get(i);
const direction = (point as any).direction;
let upi: any;
upi = ups[i] || up || v3().crossVecs(right, direction);
let righti = right;
if (!right)
righti = 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) {
const mholes = applyMat4(options.holes, _matrix, false);
newholes.push(mholes);
}
}
const geo: IGeometry = linkSides({
shapes: shapes.map(e => 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;
}
/**
* 挤压
* @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_obsolete<T extends Vec3>(shape: ArrayList<T>, arg_path: Array<Vec3> | any, options: IExtrudeOptions = defaultExtrudeOption) {
options = {
...defaultExtrudeOption,
...options
}
if (arg_path.length < 2) { throw ("路径节点数必须大于2") }
var isCCW = vector.isCCW(shape);
if (!isCCW)
shape.reverse();
var normal = options.normal;
var startSeal = clone(shape);
var shapepath = new Path(shape as any);
var insertNum = 0;
for (let 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((<any>shape).get(-1).clone());
//新加起始点纹理拉伸
shape.unshift(shape.first.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);
if (options.fixedY) {
var v = Vec3.UnitX;
rotateByUnitVectors([v], normal!, dir);
var v1 = v.clone();
v1.y = 0;
rotateByUnitVectors(newShape, v, v1);
}
translate(newShape, node);
shapeArray.push(newShape);
}
const gindex = { index: 0 };
var vertices = flat(shapeArray);
indexable(vertices, gindex);
var { index } = linkSides({ shapes: shapeArray, shapeClosed: options.shapeClosed, pathClosed: options.isClosed2, orgShape: shape as any });
shapepath = new Path(shape as any);
var uvs = [];
for (let i = 0; i < path.length; i++) {
for (let j = 0; j < shapepath.length; j++) {
uvs.push(shapepath.get(j).tlen * options.textureScale!.x, path.get(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);
if (options.fixedY) {
var v = Vec3.UnitX;
rotateByUnitVectors([v], normal!, path[0].tangent);
var v1 = v.clone();
v1.y = 0;
rotateByUnitVectors(startSeal, v, v1);
}
translate(startSeal, path[0])
rotateByUnitVectors(endSeal, normal!, path.get(-1).tangent);
if (options.fixedY) {
var v = Vec3.UnitX;
rotateByUnitVectors([v], normal!, path.get(-1).tangent);
var v1 = v.clone();
v1.y = 0;
rotateByUnitVectors(endSeal, v, v1);
}
translate(endSeal, path.get(-1));
var sealStartTris = triangulation(sealUv, [], { normal: normal! });
sealStartTris.reverse();
if (options.sealStart)
indexable(startSeal, gindex);
if (options.sealEnd)
indexable(endSeal, gindex);
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);
index!.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();
index!.push(...sealEndTris);
for (let i = 0; i < sealUv.length; i++)
uvs.push(sealUv[i].x, sealUv[i].y);
}
return {
vertices,
index,
uvs
};
}
export enum JoinType {
Square = 0,
Round,
Miter,
Bevel = 0,
}
export enum EndType {
Square,
Round,
Butt
}
export interface IExtrudeOptionsNext {
shape: Array<Vec3>;//shape默认的矩阵为正交矩阵
path: Array<Vec3 | IVec3>;
up?: Array<Vec3 | IVec3> | Vec3 | IVec3;
right?: Array<Vec3> | Vec3;
shapeClosed?: boolean;//闭合为多边形 界面
pathClosed?: boolean;//首尾闭合为圈
textureEnable?: boolean;
shapeCenter?: Vec3; //shape的中心点 模型是零点
smoothAngle?: number;
enableSmooth?: boolean;
sealStart?: boolean;
sealEnd?: boolean;
normal?: Vec3,
autoIndex?: boolean,
axisPlane?: AxisPlane,
generateUV?: boolean,
index?: { index: number },
holes?: Array<Vec3 | IVec3 | Vec2 | IVec2>[]
jtType?: JoinType;
etType?: EndType
bevelSize?: any;
}
/**
* 将路径看做挤压操作中心
*
* @param shape
* @param followPath
* @param options
*/
export function extrudeNext(options: IExtrudeOptionsNext) {
options = {
sealEnd: true, sealStart: true, shapeClosed: true, pathClosed: false,
generateUV: true,
autoIndex: true,
axisPlane: AxisPlane.XY,
smoothAngle: 30 * RADIANS_PER_DEGREE,
enableSmooth: false,
...options
}
const path = options.shapeCenter ? translate(options.path, options.shapeCenter!, false) : options.path;
const shape = options.shape;
unique(path, (a, b) => a.equals(b));
unique(shape, (a, b) => a.equals(b));
const pathPath: Path<Vec3> = new Path(path, options.pathClosed, true);
const starti = options.shapeClosed ? 0 : 1;
let shapePath: Path<Vec3> | any = new Path(shape, options.shapeClosed);
if (options.enableSmooth)
for (let 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.UnitZ;
if (isUndefined(shapePath.first.z)) {
shapePath.array = shapePath.array.map((e: any) => v3(e.x, e.y, 0));
}
var up = options.up;
var right = options.right;
const shapes = [], newholes: any = [];
const accMat = m4();
/**
* 如果路径闭合 要考虑首尾shape矩阵变化后还能一致吻合
*/
switch (options.jtType) {
case JoinType.Square: //切角
break;
case JoinType.Round://圆角
break;
case JoinType.Miter://直角
for (let i = 0; i < pathPath.length; i++) {
const p: Vec3 = pathPath.get(i);
const dir: Vec3 = (p as any).direction;
const bdir: Vec3 = (p as any).bdirection;
const bnormal: Vec3 = (p as any).bnormal;
const normal: Vec3 = (p as any).normal;
//相邻两个向量发生的旋转
if (i === 0) {
_quat.setFromUnitVecs(Vec3.UnitZ, dir);
}
else { _quat.setFromUnitVecs(pathPath.get(i - 1).direction, dir); }
let new_shape: Path<Vec3> = shapePath.clone();
//旋转
_matrix.makeRotationFromQuat(_quat);
accMat.premultiply(_matrix)
_quat.setFromUnitVecs(dir, bdir);
_matrix.makeRotationFromQuat(_quat);
_matrix.multiply(accMat);
// /旋转到原地缩放----开始-----------------------
let cosA = dir.dot(bdir);
const shear = 1 / cosA;
_vec1.crossVecs(normal, bdir);
_matrix1.copy(_matrix);
_matrix1.invert();
_vec1.applyMat4(_matrix1);
_quat.setFromUnitVecs(_vec1, 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;
}
const geo: IGeometry = linkSides({
shapes: shapes.map((e: Path<Vec3>) => 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;
}