/* * @Author : 赵耀圣 * @Date : 2020-12-10 15:01:42 * @QQ : 549184003 * @LastEditTime : 2021-09-07 15:40:10 * @FilePath : \cesium-taji-dabaod:\github\cga.js\src\struct\3d\Path.ts */ import { Vec3, v3 } from '../../math/Vec3'; import { Point } from './Point'; import { clamp, delta6 } from '../../math/Math'; import { Polyline } from './Polyline'; import { isDefined } from '../../utils/types'; // import { Polyline } from './PolyLine'; import { Polygon } from './Polygon'; import { ArrayList } from '../data/ArrayList'; import { Mat4 } from '../../math/Mat4'; import { applyMat4 } from '../../alg/pointset'; import { scale } from '../../alg/common'; export interface IDistanceResut { isNode: boolean;//是否在节点上 point: Vec3; direction: Vec3; } export class Path extends ArrayList { _closed: boolean; _calcNoraml: boolean = false /** * * @param vs 假定是没有重复的点集 * @param closed * @param calcNormal */ constructor(vs: Array | ArrayList, closed: boolean = false, calcNormal: boolean = false) { super(vs); this._closed = closed; this.init(calcNormal); } init(calcNormal: boolean) { if (this.length === 0) return this.get(0).len = 0; this.get(0).tlen = 0; const end = this.length; for (let i = 0; i < end; i++) { const e = this.get(i); if (i !== 0) { e.len = this.get(i).distanceTo(this.get(i - 1)); e.tlen = this.get(i - 1).tlen + e.len; } this.get(i).direction = this.get((i + 1) % this.length).clone().sub(this.get(i)).normalize(); } if (!this._closed) { this.get(-1).direction.copy(this.get(-2).direction); } if (calcNormal) { for (let i = 0; i < end; i++) { const d1: Vec3 = this.get(i - 1).direction; const d2: Vec3 = this.get(i).direction; // if (Math.abs(d1.dot(d2) - 1) > delta6) { //应该同时考虑长度差 //normal是两条线段所在的平面的法线 //bdirection是两条方向线的等分线 //TODO const normal = new Vec3(); normal.crossVecs(d1, d2).normalize(); this.get(i).normal = normal; const bdir = v3().addVecs(d1, d2).normalize(); this.get(i).bdirection = bdir; this.get(i).bnormal = v3().crossVecs(bdir, normal).normalize(); // } } if (!this._closed) { //不闭合路径 最后一个点没有 this.get(-1).bdirection = v3() this.get(-1).normal = v3() this.get(-1).bnormal = v3() } if (!this._closed) { // 不闭合的情况下怎么样去计算端点的up和normal this.get(0).normal.copy(this.get(1).normal) this.get(0).bdirection.copy(this.get(0).direction) let bdir = this.get(0).bdirection; this.get(0).bnormal.crossVecs(bdir, this.get(0).normal) this.get(-1).normal.copy(this.get(-2).normal) this.get(-1).bdirection.copy(this.get(-1).direction) bdir = this.get(-1).bdirection; this.get(-1).bnormal.crossVecs(bdir, this.get(-1).normal).normalize(); } } } set closed(val: boolean) { this._closed = val; } get closed() { return this._closed; } get tlen() { if (this.length === 0) return 0; return Math.max(this.get(-1).tlen, this.get(0).tlen); } applyMat4(mat4: Mat4) { applyMat4(this._array, mat4); } scale(x: number, y: number, z: number) { scale(this._array, v3(x, y, z), true); } /** * 截取一段从from到to的path * @param {Number} from * @param {Number} to */ splitByFromToDistance(from: number = 0, to: number = 0) { if (to <= from) return null; var newPath = new Path([]); for (let i = 0; i < this.length - 1; i++) { const pt = this.get(i); const ptnext = this.get(i + 1); if (pt.tlen <= from && ptnext.tlen >= from) { var v3 = new Vec3().lerpVecs(pt, ptnext, (from - pt.tlen) / (ptnext.tlen - pt.tlen)); newPath.add(v3); } if (pt.tlen > from && pt.tlen < to) { newPath.add(pt.clone()); } if (pt.tlen <= to && ptnext.tlen >= to) { var v3 = new Vec3().lerpVecs(pt, ptnext, (to - pt.tlen) / (ptnext.tlen - pt.tlen)); newPath.add(v3); return newPath; } } return newPath; } /** * 从起点出发到距离等于distance位置 的坐标 二分查找 * @param {Number} distance */ getPointByDistance(arg_distance: number, left: number = 0, right: number = this.length - 1): IDistanceResut | null { const distance = clamp(arg_distance, 0, this.get(-1).tlen); if (distance !== arg_distance) return null; if (right - left === 1) { return { isNode: false,//是否在节点上 point: new Vec3().lerpVecs(this.get(left), this.get(right), (distance - this.get(left).tlen) / this.get(right).len), direction: this.get(left).diretion, } } var mid = (left + right) >> 1; if (this.get(mid).tlen > distance) return this.getPointByDistance(distance, left, mid); else if (this.get(mid).tlen < distance) return this.getPointByDistance(distance, mid, right); else return { isNode: true,//是否在节点上 point: new Vec3().lerpVecs(this.get(left), this.get(right), (distance - this.get(left).tlen) / this.get(right).len), direction: this.get(left).direction } } /** * 从起点出发到距离等于distance位置 的坐标 二分查找 * @param {Number} distance */ getPointByDistancePure(arg_distance: number, left: number = 0, right: number = this.length - 1): Vec3 | null { const distance = clamp(arg_distance, 0, this.get(-1).tlen); if (distance !== arg_distance) return null; if (right - left === 1) { return new Vec3().lerpVecs(this.get(left), this.get(right), (distance - this.get(left).tlen) / this.get(right).len); } var mid = (left + right) >> 1; if (this.get(mid).tlen > distance) return this.getPointByDistancePure(distance, left, mid); else if (this.get(mid).tlen < distance) return this.getPointByDistancePure(distance, mid, right); else return this.get(mid).clone(); } /** * 平均切割为 splitCount 段 * @param {Number} splitCount * @returns {Path} 新的path */ splitAverage(splitCount: number): Path { var tlen = this.last.tlen; var perlen = tlen / splitCount; var res: Vec3[] = []; var curJ = 0 for (var i = 0; i <= splitCount; i++) { var plen = i * perlen; for (let j = curJ; j < this.length - 1; j++) { if (this.get(j).tlen <= plen && this.get(j + 1).tlen >= plen) { var p = new Vec3().lerpVecs(this.get(j), this.get(j + 1), (plen - this.get(j).tlen) / (this.get(j + 1).len)) res.push(p); curJ = j; break; } } } return new Path(res); } /** * 通过测试 * 平均切割为 splitCount 段 * @param {Number} splitCount * @param {Boolean} integer 是否取整 * @returns {Path} 新的path */ splitAverageLength(splitLength: number, integer = true) { var tlen = this.last.tlen; var count = tlen / splitLength; if (integer) count = Math.round(count); return this.splitAverage(count); } /** * * @param {...any} ps */ add(...ps: Vec3[] | Point[]) { if (this.length == 0) { const firstpt = ps.shift(); this.push(firstpt); this.get(0).len = 0; this.get(0).tlen = 0; } for (let i = 0; i < ps.length; i++) { const pt: any = ps[i]; pt.len = pt.distanceTo(this.get(-1)); pt.tlen = this.get(-1).tlen + pt.len; pt.direction = pt.clone().sub(this.get(-1)).normalize(); if (!this.get(-1).direction) this.get(-1).direction = pt.clone().sub(this.get(-1)).normalize(); else this.get(-1).direction.copy(pt.direction); this.push(pt); } } /** * @description : 计算一段线段的总长度 * @param {ArrayLike} ps * @return {number} 总长度 */ static totalMileages(ps: ArrayLike): number { var alldisance = 0 for (let i = 0, len = ps.length - 1; i < len; i++) { alldisance += ps[i + 1].distanceTo(ps[i]); } return alldisance; } /** * @description : 获取没一点的里程 里程是指从第一个点出发的长度 * @param {ArrayLike} ps 里程上的点集 * @param {boolean} normalize 是否归一化 * @return {number[]} 每一个点的里程数组 * @example : */ static getPerMileages(ps: ArrayLike, normalize: boolean = false, totalMileage?: number): number[] { const res: number[] = []; let mileages = 0 res.push(mileages); for (let i = 0, len = ps.length - 1; i < len; i++) { mileages += ps[i + 1].distanceTo(ps[i]); res.push(mileages); } if (normalize) { const tl = isDefined(totalMileage) ? totalMileage : this.totalMileages(ps); for (let i = 0, len = ps.length; i < len; i++) { res[i] /= tl!; } } return res; } }