( point1: THREE.Vector3, point2: THREE.Vector3, point3: THREE.Vector3, flipNormal = false, targetUp: THREE.Vector3 = new THREE.Vector3(0, 1, 0) )
| 53 | * The plane is defined by three points, and rotation is about their centroid. |
| 54 | */ |
| 55 | export function computeNormalAlignment( |
| 56 | point1: THREE.Vector3, |
| 57 | point2: THREE.Vector3, |
| 58 | point3: THREE.Vector3, |
| 59 | flipNormal = false, |
| 60 | targetUp: THREE.Vector3 = new THREE.Vector3(0, 1, 0) |
| 61 | ): Sim3d { |
| 62 | const v1 = new THREE.Vector3().subVectors(point2, point1); |
| 63 | const v2 = new THREE.Vector3().subVectors(point3, point1); |
| 64 | const normal = new THREE.Vector3().crossVectors(v1, v2); |
| 65 | |
| 66 | if (normal.lengthSq() < 1e-10) { |
| 67 | return identitySim3d(); |
| 68 | } |
| 69 | |
| 70 | normal.normalize(); |
| 71 | |
| 72 | if (flipNormal) { |
| 73 | normal.negate(); |
| 74 | } |
| 75 | |
| 76 | const upNormalized = targetUp.clone().normalize(); |
| 77 | const dot = normal.dot(upNormalized); |
| 78 | if (Math.abs(dot - 1) < 1e-6) { |
| 79 | return identitySim3d(); |
| 80 | } |
| 81 | |
| 82 | let rotation: THREE.Quaternion; |
| 83 | |
| 84 | if (Math.abs(dot + 1) < 1e-6) { |
| 85 | const perpAxis = new THREE.Vector3(1, 0, 0); |
| 86 | if (Math.abs(upNormalized.dot(perpAxis)) > 0.9) { |
| 87 | perpAxis.set(0, 0, 1); |
| 88 | } |
| 89 | rotation = new THREE.Quaternion().setFromAxisAngle(perpAxis, Math.PI); |
| 90 | } else { |
| 91 | rotation = new THREE.Quaternion().setFromUnitVectors(normal, upNormalized); |
| 92 | } |
| 93 | |
| 94 | const centroid = new THREE.Vector3() |
| 95 | .add(point1) |
| 96 | .add(point2) |
| 97 | .add(point3) |
| 98 | .divideScalar(3); |
| 99 | const rotatedCentroid = centroid.clone().applyQuaternion(rotation); |
| 100 | const translation = new THREE.Vector3().subVectors(centroid, rotatedCentroid); |
| 101 | |
| 102 | return { |
| 103 | scale: 1, |
| 104 | rotation, |
| 105 | translation, |
| 106 | }; |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Compute a transform that aligns a detected floor plane with a target axis |
no test coverage detected