import * as THREE from 'three'; import { Geometry, Vector, Vector2, Vector3, Box3 } from 'three'; import { Matrix2 } from './Matrix2'; export const cZeroVec = new THREE.Vector3(); export const cXAxis = new THREE.Vector3(1, 0, 0); export const cYAxis = new THREE.Vector3(0, 1, 0); export const cZAxis = new THREE.Vector3(0, 0, 1); /** * 旋转一个点,旋转中心在原点 * * @export * @param {Vector3} pt 点 * @param {number} ang 角度. * @returns {Vector3} 返回pt不拷贝. */ export function rotatePoint(pt: Vector3, ang: number): Vector3 { new Matrix2().setRotate(ang).applyVector(pt); return pt; } export function equaln(v1: number, v2: number, fuzz = 1e-3) { return Math.abs(v1 - v2) < fuzz; } export function equal(v1: T, v2: T) { return v1.distanceToSquared(v2) < 1e-8; } export function fixAngle(an: number, fixAngle: number, fuzz: number = 0.1) { if (an < 0) an += Math.PI * 2; an += fuzz; let rem = an % fixAngle; if (rem < fuzz * 2) { an -= rem; } else { an -= fuzz; } return an; } /** * 按照极坐标的方式移动一个点 * * @export * @template * @param {T} v 向量(2d,3d) * @param {number} an 角度 * @param {number} dis 距离 * @returns {T} */ export function polar(v: T, an: number, dis: number): T { v.x += Math.cos(an) * dis; v.y += Math.sin(an) * dis; return v; } export function angle(v: Vector3 | Vector2) { if (equaln(v.y, 0) && v.x > 0) return 0; let angle = Math.atan2(v.y, v.x); if (angle < 0) angle += Math.PI * 2; return angle; } /** * 求两个向量的夹角,顺时针为负,逆时针为正 * * @param {THREE.Vector3} v1 * @param {THREE.Vector3} v2 * @param {THREE.Vector3} [ref] 参考向量,如果为世界坐标系则为0,0,1 * @returns */ export function angleTo(v1: THREE.Vector3, v2: THREE.Vector3, ref: THREE.Vector3 = new THREE.Vector3(0, 0, 1)) { if (!ref.equals(new Vector3(0, 0, 1))) { //任意轴坐标系. 使用相机的构造矩阵. ref.multiplyScalar(-1); let up = getLoocAtUpVec(ref); let refOcs = new THREE.Matrix4(); refOcs.lookAt(cZeroVec, ref, up); let refOcsInv = new THREE.Matrix4().getInverse(refOcs); v1.applyMatrix4(refOcsInv); v2.applyMatrix4(refOcsInv); v1.z = 0; v2.z = 0; } if (v1.equals(cZeroVec) || v2.equals(cZeroVec)) return 0; let cv = new Vector3().crossVectors(v1, v2).normalize(); return cv.z === 0 ? v1.angleTo(v2) : v1.angleTo(v2) * cv.z; } export function getLoocAtUpVec(dir: THREE.Vector3): THREE.Vector3 { if (dir.equals(cZeroVec)) { throw ("zero vector") } let norm = dir.clone().normalize(); if (norm.equals(cZAxis)) { return new THREE.Vector3(0, 1, 0); } else if (norm.equals(cZAxis.clone().negate())) { return new THREE.Vector3(0, -1, 0); } else { let xv: THREE.Vector3 = new THREE.Vector3(); xv.crossVectors(cZAxis, norm); let up = new THREE.Vector3(); up.crossVectors(norm, xv); return up; } } export function createLookAtMat4(dir: THREE.Vector3): THREE.Matrix4 { let up = getLoocAtUpVec(dir); let mat = new THREE.Matrix4(); mat.lookAt(cZeroVec, dir, up); return mat; } export function isParallelTo(v1: THREE.Vector3, v2: THREE.Vector3) { return v1.clone().cross(v2).lengthSq() < 1e-9; } export function ptToString(v: THREE.Vector3, fractionDigits: number = 3): string { return v.toArray().map(o => { return o.toFixed(fractionDigits) }).join(",") } export function midPoint(v1: THREE.Vector3, v2: THREE.Vector3): THREE.Vector3 { return v1.clone().add(v2).multiplyScalar(0.5); } export function midPoint2(v1: THREE.Vector2, v2: THREE.Vector2): THREE.Vector2 { return v1.clone().add(v2).multiplyScalar(0.5); } export function midPtCir(v1: THREE.Vector3, v2: THREE.Vector3) { let baseline = new Vector3(1, 0, 0); let outLine = v2.clone().sub(v1); let ang = angleTo(baseline, outLine) / 2; let midLine = rotatePoint(outLine, -ang); return v1.clone().add(midLine); } /** * 获得Three对象的包围盒. * @param obj * @param [updateMatrix] 是否应该更新对象矩阵 * @returns box */ export function GetBox(obj: THREE.Object3D, updateMatrix?: boolean): THREE.Box3 { let box = new Box3(); if (updateMatrix) obj.updateMatrixWorld(false); if (!obj.visible) return box; obj.traverse(o => { //因为实体Erase时,实体仍然保存在Scene中. if (o.visible === false) return; //@ts-ignore let geo = o.geometry as BufferGeometry; if (geo) { if (!geo.boundingBox) geo.computeBoundingBox(); box.union(geo.boundingBox.clone().applyMatrix4(o.matrixWorld)); } }); return box; } export function GetBoxArr(arr: Array): THREE.Box3 { let box = new Box3(); for (let o of arr) { let b = GetBox(o); if (!b.isEmpty()) box.union(b); } return box; } export function MoveMatrix(v: THREE.Vector3): THREE.Matrix4 { let mat = new THREE.Matrix4(); mat.makeTranslation(v.x, v.y, v.z); return mat; } export function getProjectDist(v1: Vector3, v2: Vector3) { let ang = v1.angleTo(v2); let dist = v1.length(); return { h: dist * Math.cos(ang), v: dist * Math.sin(ang) } } //获得输入点在2线组成的4个区间的位置 export function getPtPostion(sp: Vector3, ep: Vector3, c: Vector3, inPt: Vector3) { let l1 = sp.clone().sub(c); let l2 = ep.clone().sub(c); let l3 = l1.clone().negate(); let l4 = l2.clone().negate(); let inputLine = inPt.clone().sub(c); let ang1 = angleTo(l1, l2); let ang2 = Math.PI; let ang3 = ang2 + Math.abs(ang1); let inputAng = angleTo(l1, inputLine); if (ang1 * inputAng < 0) { inputAng = (Math.PI * 2 - Math.abs(inputAng)); } ang1 = Math.abs(ang1); inputAng = Math.abs(inputAng); if (inputAng <= ang1) { return { sp, ep }; } else if (inputAng > ang1 && inputAng <= ang2) { return { sp: c.clone().add(l3), ep } } else if (inputAng > ang2 && inputAng <= ang3) { return { sp: c.clone().add(l3), ep: c.clone().add(l4) } } else { return { sp, ep: c.clone().add(l4) }; } } export function angleAndX(v: Vector3 | Vector2) { return v.x ? Math.atan(v.y / v.x) : Math.PI / 2; } /** * 将角度调整为0-2pi之间 * * @export * @param {number} an */ export function angleTo2Pi(an: number) { an = an % (Math.PI * 2); if (an < 0) an += Math.PI * 2 return an; } export function updateGeometry(l: THREE.Line | THREE.Mesh, geometry: Geometry) { let geo = l.geometry as THREE.Geometry; geo.dispose(); l.geometry = geometry; geometry.verticesNeedUpdate = true; geometry.computeBoundingSphere(); }