如何将矢量旋转 90 度到垂直平面,然后再从该平面旋转 15 度?

How can I rotate a vector 90 degrees into a perpendicular plane, and then 15 degrees free from that plane?

我最终想要的是一个向量,给出下图中绿线的方向,只知道黄点和绿点的位置。
更具体地说,它的角度可以是随机的,只要它的端点在圆柱体的绿蓝色表面上的某个地方结束。因此,圆柱体周围 360° 自由,大约 15° 限制在圆柱体的边缘。

圆柱垂直于黄点和绿点的直线。

长度不重要,方向重要

我的主要问题是我不知道如何从矢量黄色到绿点,再到垂直于它的任何矢量。

PS None 这些东西在 x y z 轴上对齐。该网格不是 xyz,只是为了帮助可视化。

代码如下:给定一个角度 theta 和两个点,它将为您提供一个从 pointStart 开始的向量,该向量垂直于从 pointStart 到 pointEnd 的向量:

function perpendicularVector(pointStart,pointEnd,theta){
    let vDiff = new THREE.Vector3(0, 0, 0)
        .subVectors(pointEnd, pointStart) 
        .normalize()
  
    let V = new THREE.Vector3(
        vDiff.y + vDiff.x * vDiff.z,
      vDiff.y * vDiff.z -vDiff.x,
      -(vDiff.x * vDiff.x) - vDiff.y * vDiff.y
    )

    return
        V   .applyAxisAngle(vDiff, theta)
            .applyAxisAngle( new THREE.Vector3().multiplyVectors(V, vDiff).normalize(), 15*Math.PI/180 )
}

这里是上面代码的一个小展示:(这个片段是故意的,因为它只是为了展示上面代码的功能)

(您可以在单击 运行 片段后出现的渲染图上使用鼠标缩放旋转和平移)

body {
  font-family: sans-serif;
  margin: 0;
  background-color: #e2cba9;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

canvas {
  width: 100%;
  height: 100%;
}
<div id="app"></div>

<script type="module">
import { OrbitControls } from "https://cdn.jsdelivr.net/npm/three@0.121.1/examples/jsm/controls/OrbitControls.js";

import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.121.1/build/three.module.js";

var scene = new THREE.Scene, theta = 0;
let point1 = new THREE.Vector3(4, 2, 1),
  point2 = new THREE.Vector3(0, 3, 3);

function perpendicularVector(e, n, t) {
  let r = new THREE.Vector3(0, 0, 0).subVectors(n, e).normalize(),
    o = new THREE.Vector3(r.y, -r.x, 0),
    i = new THREE.Vector3(r.x * r.z, r.y * r.z, -r.x * r.x - r.y * r.y);
  var a = o.multiplyScalar(Math.cos(t)).add(i.multiplyScalar(Math.sin(t)));
  return a.add(e), a
}

function pointAtCoords(e, n) {
  let t = new THREE.MeshBasicMaterial({ color: n }),
    r = new THREE.SphereGeometry(.1, 8, 8),
    o = new
  THREE.Mesh(r, t);
  return o.position.add(e), o
}

function lineFromAtoB(e, n, t) {
  let r = new THREE.LineBasicMaterial({ color: t }),
    o = [];
  o.push(e), o.push(n);
  let i = (new THREE.BufferGeometry).setFromPoints(o);
  return new THREE.Line(i, r)
}

var renderer = new THREE.WebGLRenderer({ antialias: !0 });
renderer.setSize(window.innerWidth, window.innerHeight), document.getElementById("app").appendChild(renderer.domElement);
var camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight,
  .1, 1e3);
camera.position.set(7, 7, 8), camera.lookAt(new THREE.Vector3), camera.position.add(new THREE.Vector3(3, 0, 3));
var controls = new OrbitControls(camera, renderer.domElement);

function drawEverything(e) {
  const n = new THREE.AxesHelper(30);
  scene.add(n);
  const t = new THREE.GridHelper(30, 30);
  t.position.add(new THREE.Vector3(15, 0, 15)), scene.add(t);
  const r = new THREE.GridHelper(30, 30);
  r.rotateX(Math.PI / 2), r.position.add(new THREE.Vector3(15, 15, 0)), scene.add(r);
  const o = new THREE.GridHelper(30, 30);
  o.rotateZ(Math.PI / 2), o.position.add(new THREE.Vector3(0, 15, 15)), scene.add(o);
  let i = new THREE.Vector3(0, 0, 0),
    a = perpendicularVector(point1, point2, e);
  scene.add(pointAtCoords(point1, 16776960)), scene.add(pointAtCoords(point2, 65280));
  var d = pointAtCoords(a, 255);
  scene.add(d), scene.add(lineFromAtoB(point1, point2, 16711935)), scene.add(lineFromAtoB(i, point1, 16711680)), scene.add(lineFromAtoB(i, point2, 16711680)), scene.add(lineFromAtoB(point1, a, 65280))
}

function animate() {
   scene = new THREE.Scene, drawEverything(theta += .1), 
   setTimeout((() => {
    requestAnimationFrame(animate)
  }), 1e3 / 30), renderer.render(scene, camera)
}

animate();
</script>

这完全可以通过一些数学计算来实现。您要查找的术语是 "Orthogonal vectors",这意味着相互垂直的向量。圆柱体半径正交于蓝点到黄点之间的线。

但是,由于您已经在使用 Three.js,您可以让它在 an Object3D 的帮助下为您完成所有艰苦的工作。

// Declare vectorA (center, green)
const vecA = new THREE.Vector3(xA, yA, zA);

// Declare vectorB (destination, yellow)
const vecB = new THREE.Vector3(xB, yB, zB);

// Create helper object
const helper = new THREE.Object3D();

// Center helper at vecA
helper.position.copy(vecA);

// Rotate helper towards vecB
helper.lookAt(vecB);

// Move helper perpendicularly along its own y-axis
const cylinderRadius = 27;
helper.translateY(cylinderRadius);

// Now you have your final position!
console.log(helper.position);

在下图中,助手 Object3D 显示为红线只是为了让您了解它的旋转和位置,但实际上它是不可见的,除非您添加 Mesh

如果你想 add/subtract 与垂直线成 15 度,你可以沿着它自己 x-axis before translateY()

const xAngle = THREE.MathUtils.degToRad(15);
helper.rotateX(xAngle);

const cylinderRadius = 27;
helper.translateY(cylinderRadius);