Нарисуйте эллипс ar c между двумя точками в Three. js - PullRequest
1 голос
/ 24 марта 2020

Я пытался нарисовать эллипс ar c между двумя произвольными точками, но в некоторых ситуациях моя реализация не работает.

Поскольку часть этого связана с математикой, я начал с вопроса этот вопрос . В принципе, если предоставить две точки и центр, вы всегда можете получить эллипс, если разрешите вращение, за исключением случаев, когда точки коллинеарны.

Решение, предложенное для этого вопроса, заключается в следующем:

  1. Переведите центр в начало координат, переведя обе точки в один и тот же вектор.
  2. Поверните обе точки на угол -alpha, который является симметрией c угла наибольшего вектора с положительной полуосью x.
  3. Решите уравнение эллипса, чтобы найти его радиусы (система двух уравнений с двумя неизвестными).
  4. Определить эллипс
  5. Повернуть эллипс назад на угол alpha и перевести обратно в его центр.

Однако у меня возникли проблемы с реализацией этого в Три. js. В документации для EllipseCurve перечислены ожидаемые параметры. Я предполагаю, что начальный угол всегда равен нулю, а затем устанавливаю конечный угол либо на угол между двумя векторами, либо на его симметрию c. Я также хочу, чтобы ar c всегда было наименьшим (т. Е. Если угол больше 180 °, я бы использовал дополнительный ar c). Я предполагаю, что центр эллипса является средней точкой между центрами ограничительных рамок фигуры.

Это мой пример кода:
https://jsfiddle.net/at5dc7yk/1/
В этом примере делается попытка создать ar c из вершины в исходной форме и той же вершины в измененная форма.
Код относительно эллипса ar c относится к классу EllipseArc, и вы можете испортить преобразование, примененное к объекту в строке 190.

В некоторых случаях это работает:
Example working

Но не все:
Example not working

1 Ответ

1 голос
/ 26 марта 2020

Просто идея с нуля, а не окончательное решение.

Когда вы клонируете и переводите объект, для построения ar c между двумя соответствующими точками вам понадобятся их координаты в мировой системе координат, и координата средней точки между центроидами объектов.

  1. Найдите среднюю точку между точками в мировом пространстве (между начальным и конечным векторами).
  2. Найдите свою проекцию на вектор перевод (это центр ar c).
  3. Найдите угол между векторами, которые вы получаете путем вычитания вектора центра результата из каждого из них.
  4. Разделите угол на величину делений - вы получите значение шага.
  5. Получите конечный вектор в качестве основы и поверните его вокруг оси (которая является нормалью треугольника, построенного с начальными, центральными, конечными векторами) в oop, умножая это значение угла шага на номер текущей итерации.

Пример кода:

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 1, 10000);
camera.position.set(0, 0, 150);
var renderer = new THREE.WebGLRenderer();
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

var controls = new THREE.OrbitControls(camera, renderer.domElement);

var shapeGeom = new THREE.ShapeBufferGeometry(new THREE.Shape(californiaPts));
shapeGeom.center();
shapeGeom.scale(0.1, 0.1, 0.1);

var shapeMat = new THREE.MeshBasicMaterial({
  color: "orange"
});
var shape = new THREE.Mesh(shapeGeom, shapeMat);
shape.updateMatrixWorld();
scene.add(shape);

var shapeClone = shape.clone();
shapeClone.position.set(25, 25, 0);
shapeClone.updateMatrixWorld();
scene.add(shapeClone);

var center = new THREE.Vector3().lerpVectors(shapeClone.position, shape.position, 0.5);
var vecStart = new THREE.Vector3();
var vecEnd = new THREE.Vector3();
var pos = shapeGeom.getAttribute("position");

for (let i = 0; i < pos.count; i++) {
  vecStart.fromBufferAttribute(pos, i);
  shape.localToWorld(vecStart);
  vecEnd.fromBufferAttribute(pos, i);
  shapeClone.localToWorld(vecEnd);
  makeArc(center, vecStart, vecEnd);
}

function makeArc(center, start, end) {

  console.log(center, start, end);


  let vM = new THREE.Vector3().addVectors(start, end).multiplyScalar(0.5);
  let dir = new THREE.Vector3().subVectors(end, start).normalize();

  let c = new THREE.Vector3().subVectors(vM, center);
  let d = c.dot(dir);
  c.copy(dir).multiplyScalar(d).add(center); // get a center of an arc

  let vS = new THREE.Vector3().subVectors(start, c);
  let vE = new THREE.Vector3().subVectors(end, c);
  let a = vS.angleTo(vE); // andgle between start and end, relatively to the new center



  let divisions = 100;
  let aStep = a / divisions;
  let pts = [];
  let vecTemp = new THREE.Vector3();
  let tri = new THREE.Triangle(start, c, end);
  let axis = new THREE.Vector3();
  tri.getNormal(axis); // get the axis to rotate around
  for (let i = 0; i <= divisions; i++) {

    vecTemp.copy(vE);
    vecTemp.applyAxisAngle(axis, aStep * i);
    pts.push(vecTemp.clone());

  }

  let g = new THREE.BufferGeometry().setFromPoints(pts);
  let m = new THREE.LineDashedMaterial({
    color: 0xff0000,
    dashSize: 1,
    gapSize: 1
  });
  let l = new THREE.Line(g, m);
  l.computeLineDistances();
  l.position.copy(c);

  scene.add(l);

}

renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});
body {
  overflow: hidden;
  margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script>
  var californiaPts = [
    new THREE.Vector2(610, 320),
    new THREE.Vector2(450, 300),
    new THREE.Vector2(392, 392),
    new THREE.Vector2(266, 438),
    new THREE.Vector2(190, 570),
    new THREE.Vector2(190, 600),
    new THREE.Vector2(160, 620),
    new THREE.Vector2(160, 650),
    new THREE.Vector2(180, 640),
    new THREE.Vector2(165, 680),
    new THREE.Vector2(150, 670),
    new THREE.Vector2(90, 737),
    new THREE.Vector2(80, 795),
    new THREE.Vector2(50, 835),
    new THREE.Vector2(64, 870),
    new THREE.Vector2(60, 945),
    new THREE.Vector2(300, 945),
    new THREE.Vector2(300, 743),
    new THREE.Vector2(600, 473),
    new THREE.Vector2(626, 425),
    new THREE.Vector2(600, 370),
    new THREE.Vector2(610, 320)
  ];
</script>

Если вы не переводите, а просто вращаете объект, в этом случае вам не нужно вычислять новый центр для каждого объекта. c, просто пропустите этот шаг, так как все центры равны центроиду объекта.

Надеюсь, я объяснил это более-менее понятно ^^

...