Как найти две точки перпендикулярные отрезку на заданном расстоянии?

Имеется две точки: c, d, через них проходит прямая. Имеется точки AB - отрезок. Вопрос: как выставить c и d таким образом, чтобы они были перпендикулярны отрезку AB?

P.S. Известны координаты только точек AB, а расстояние между c и d составляет 1.

введите сюда описание изображения


Ответы (3 шт):

Автор решения: MBo

Направляющий вектор

dx, dy = B.x-A.x, B.y - A.y

Длина

len = sqrt(dx*dx+dy*dy)

Нормализованный вектор (единичной длины)

udx = dx / len
udy = dy / len

Перпендикулярный единичный вектор

nx = - udy
ny =  udx

Точки (C, D) на перпендикуляре на расстоянии R

A.x + nx * R, A.y + ny * R 
A.x - nx * R, A.y - ny * R 
→ Ссылка
Автор решения: Kromster

Алгоритм:

  1. Все расчеты удобнее вести от точки A, принимая её координаты за 0:0
  2. Найти перпендикуляр к вектору AB
  3. Нормализовать его (чтобы он был единичной длины)
  4. Умножить на половину ширины - это ваше c
  5. Умножить на минус половину ширины - это ваше d
→ Ссылка
Автор решения: prisoner849

Раз уж речь про three.js и 2D, то:

var a = new THREE.Vector2(...);
var b = new THREE.Vector2(...);

var ab = new THREE.Vector2().subVectors(b, a); //базовый вектор

var c = new THREE.Vector2(-ab.y, ab.x).setLength(0.5); // перпендикуляр к (x, y) есть (-y, x)
var d = new THREE.Vector2().copy(c).negate(); // можно использовать d = c.clone().negate()

c.add(a);
d.add(a);

И если 2D на XY по каким-то причинам не мил, то можно это организовать на XZ в 3D:

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
import * as THREE from "https://cdn.skypack.dev/[email protected]";
import { OrbitControls } from "https://cdn.skypack.dev/[email protected]/examples/jsm/controls/OrbitControls";

let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 1000);
camera.position.set(0, 10, 0);
let renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.enablePan = false;

scene.add(new THREE.GridHelper());

let ptsBase = [new THREE.Vector3(1, 0, -2), new THREE.Vector3(4, 0, -4)];

let ptsNorm = ptsBase.map((p, idx, arr) => {
  let v = new THREE.Vector3();
  getNormAt(v, 0.5 * (idx % 2 == 0 ? 1 : -1), arr);
  v.add(ptsBase[0]);
  return v;
});

let baseLine = new THREE.Line(
  new THREE.BufferGeometry()
    .setFromPoints(ptsBase)
    .setAttribute(
      "color",
      new THREE.Float32BufferAttribute([0, 0, 1, 0, 1, 1], 3)
    ),
  new THREE.LineBasicMaterial({ vertexColors: true })
);
let normLine = new THREE.Line(
  new THREE.BufferGeometry()
    .setFromPoints(ptsNorm)
    .setAttribute(
      "color",
      new THREE.Float32BufferAttribute([1, 0, 0, 1, 1, 0], 3)
    ),
  new THREE.LineBasicMaterial({ vertexColors: true })
);
scene.add(baseLine, normLine);

renderer.setAnimationLoop(() => {
  controls.update();
  renderer.render(scene, camera);
});

function getNormAt(vector, distance, basePoints) {
  let v3 = new THREE.Vector3().subVectors(basePoints[1], basePoints[0]);
  vector.set(v3.z, 0, -v3.x).setLength(distance); // вся магия тут
}
</script>

A - синий, B - аква, С - красный, D - желтый

→ Ссылка