Пересчет координат вершин объекта

147
31 августа 2018, 00:50

Ориентируясь на пример кода из ответа
Немного поэкспериментировал с кодом, я рассеял точки по экрану

textGeo.vertices.forEach(function (vertex) {
    vertex.x = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5);
    vertex.y = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5);
    vertex.z = THREE.Math.randFloat(-10, 10)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5);
  });

Добавил пару функций и изменил функцию render

function setPosition(vertex, curent, orign, step) {
  let curentPoint = Math.round(curent * 10) / 10;
  let orignPoint = Math.round(orign * 10) / 10;
  if (curentPoint !== orignPoint) {
    curent = orign < curent ? curent - step : curent + step;
  } else {
    curent = orign;
    vertex.complate = true;
  }
  return curent;
}
function isComplated(vertices) {
  let result = true;
  vertices.forEach(function(vertex, i) {
    if(!vertex.complate) {
      result = false;
    }
  });
  return result;
}
function render() {
  let animationId = requestAnimationFrame(render);
  if (!isComplated(textGeo.vertices)) {
    textGeo.vertices.forEach(function (vertex) {
      vertex.x = setPosition(vertex, vertex.x, vertex.startPoint.x, 0.1);
      vertex.y = setPosition(vertex, vertex.y, vertex.startPoint.y, 0.1);
      vertex.z = setPosition(vertex, vertex.z, vertex.startPoint.z, 0.1);
    });
  } else {
    cancelAnimationFrame(animationId);
  }
  textGeo.verticesNeedUpdate = true;
  renderer.render(scene, camera);
}

Идея была следующей:
Рассеиваем точки по экрану, а затем начинаем их возвращать на начальные места, когда все точки займут свое начальное положение завершить анимацию остановкой AnimationFrame. Но проблема в том, что cancelAnimationFrame(animationId) вызывается раньше, чем все точки возвращаются на свои места.

Рабочий пример:

var scene = new THREE.Scene(); 
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); 
camera.position.set(0, 10, 20); 
var renderer = new THREE.WebGLRenderer({ 
  antialias: true 
}); 
renderer.setSize(window.innerWidth, window.innerHeight); 
document.body.appendChild(renderer.domElement); 
 
// var controls = new OrbitControls(camera, renderer.domElement); 
camera.lookAt(scene.position); 
 
var light = new THREE.DirectionalLight(0xffffff, 2); 
light.position.setScalar(100); 
scene.add(light); 
 
var textGeo = null; 
var textPoints = null; 
var loader = new THREE.FontLoader(); 
loader.load('https://threejs.org/examples/fonts/droid/droid_serif_bold.typeface.json', function (response) { 
  var font = response; 
  setText(font); 
  render(); 
}); 
 
function setText(font) { 
  textGeo = new THREE.TextGeometry('ABC', { 
    font: font, 
    size: 4, 
    height: 0.5, 
    curveSegments: 4, 
    bevelEnabled: false, 
    bevelSize: 10, 
    bevelThickness: 50 
  }); 
  textGeo.computeBoundingBox(); 
  textGeo.computeVertexNormals(); 
  textGeo.center(); 
 
  fillWithPoints(textGeo, 10); 
 
  textGeo.vertices.forEach(function (vertex) { 
    vertex.startPoint = vertex.clone(); 
    vertex.direction = vertex.clone().normalize(); 
  }) 
 
  textGeo.vertices.forEach(function (vertex) { 
    vertex.x = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); 
    vertex.y = THREE.Math.randFloat(-20, 20)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); 
    vertex.z = THREE.Math.randFloat(-10, 10)//.copy(vertex.startPoint).addScaledVector(vertex.direction, 5 + Math.sin(Date.now() * 0.001) * 5); 
  }); 
  window.p = textGeo.vertices[0] 
  //console.log(textGeo.vertices[0]); 
 
  //textGeo.verticesNeedUpdate = true; 
 
  //textGeo.applyMatrix( new THREE.Matrix4().makeTranslation( 1, 1, 1 ) ); 
 
  textPoints = new THREE.Points(textGeo, new THREE.PointsMaterial({ 
    color: 0xf00008, 
    size: 0.1 
  })); 
  scene.add(textPoints); 
} 
 
function fillWithPoints(geometry, pointNumber) { 
  geometry.computeBoundingBox(); 
  for (var i = 0; i < pointNumber; i++) { 
    setRandomPoint(geometry); 
  } 
} 
 
function setRandomPoint(geometry) { 
  var point = new THREE.Vector3( 
    THREE.Math.randFloat(geometry.boundingBox.min.x, geometry.boundingBox.max.x), 
    THREE.Math.randFloat(geometry.boundingBox.min.y, geometry.boundingBox.max.y), 
    THREE.Math.randFloat(geometry.boundingBox.min.z, geometry.boundingBox.max.z) 
  ); 
  //console.log(point); 
  if (isPointInside(point, geometry)) { 
    geometry.vertices.push(point); 
  } else { 
    setRandomPoint(geometry); 
  } 
} 
 
var a = new THREE.Vector3(); 
var b = new THREE.Vector3(); 
var c = new THREE.Vector3(); 
var face = new THREE.Face3(); 
 
function isPointInside(point, geometry) { 
  var retVal = false; 
  for (var i = 0; i < geometry.faces.length; i++) { 
    face = geometry.faces[i]; 
    a = geometry.vertices[face.a]; 
    b = geometry.vertices[face.b]; 
    c = geometry.vertices[face.c]; 
    //console.log(face, a, b, c); 
    if (ptInTriangle(point, a, b, c)) { 
      var retVal = true; 
      break; 
    } 
  } 
  return retVal; 
} 
 
function ptInTriangle(p, p0, p1, p2) { 
  // credits: http://jsfiddle.net/PerroAZUL/zdaY8/1/ 
  var A = 1 / 2 * (-p1.y * p2.x + p0.y * (-p1.x + p2.x) + p0.x * (p1.y - p2.y) + p1.x * p2.y); 
  var sign = A < 0 ? -1 : 1; 
  var s = (p0.y * p2.x - p0.x * p2.y + (p2.y - p0.y) * p.x + (p0.x - p2.x) * p.y) * sign; 
  var t = (p0.x * p1.y - p0.y * p1.x + (p0.y - p1.y) * p.x + (p1.x - p0.x) * p.y) * sign; 
 
  return s > 0 && t > 0 && (s + t) < 2 * A * sign; 
} 
 
function setPosition(vertex, curent, orign, step) { 
  let curentPoint = Math.round(curent * 10) / 10; 
  let orignPoint = Math.round(orign * 10) / 10; 
  if (curentPoint !== orignPoint) { 
    curent = orign < curent ? curent - step : curent + step; 
  } else { 
    curent = orign; 
    vertex.complate = true; 
  } 
  return curent; 
} 
 
function isComplated(vertices) { 
  let result = true; 
  vertices.forEach(function(vertex, i) { 
    if(!vertex.complate) { 
      result = false; 
    } 
  }); 
  return result; 
} 
function render() { 
  let animationId = requestAnimationFrame(render); 
  if (!isComplated(textGeo.vertices)) { 
    textGeo.vertices.forEach(function (vertex) { 
      vertex.x = setPosition(vertex, vertex.x, vertex.startPoint.x, 0.1); 
      vertex.y = setPosition(vertex, vertex.y, vertex.startPoint.y, 0.1); 
      vertex.z = setPosition(vertex, vertex.z, vertex.startPoint.z, 0.1); 
    }); 
  } else { 
    cancelAnimationFrame(animationId); 
  } 
   
  textGeo.verticesNeedUpdate = true; 
   
  /* complated = isComplated(textGeo.vertices); 
  if (complated) { 
    //console.log(complated); 
    cancelAnimationFrame(animationId); 
    console.log('end') 
  } */ 
   
 
  renderer.render(scene, camera); 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script>

Правильным ли я путем иду? Либо же такие вещи делаются иначе?

Answer 1

Известна рандомная позиция точки, известна конечная позиция точки, известно расстояние между ними, известна скорость. Можем найти время анимации разделив наибольшее из всех расстояний на скорость, и как только текущее время анимации стало больше или равно расчетному - производим какое-то действие.

Кроме того, есть метод .clampLength(), упрощающий жизнь и не дающий длине вектора приращения выходить за заданные пределы:

var scene = new THREE.Scene(); 
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 1000); 
camera.position.set(2, 3, 5); 
var renderer = new THREE.WebGLRenderer({ 
  antialias: true 
}); 
renderer.setSize(window.innerWidth, window.innerHeight); 
document.body.appendChild(renderer.domElement); 
 
var controls = new THREE.OrbitControls(camera, renderer.domElement); 
 
var tempDist = new THREE.Vector3(); 
var boxGeom = new THREE.BoxGeometry(2, 2, 2, 10, 10, 10); 
boxGeom.vertices.forEach(v => { 
  v.init = v.clone(); 
  v.random = new THREE.Vector3(THREE.Math.randFloatSpread(10), THREE.Math.randFloatSpread(10), THREE.Math.randFloatSpread(5)); 
  v.dir = new THREE.Vector3().copy(v.init).sub(v.random).normalize(); 
  v.dist = tempDist.copy(v.init).sub(v.random).length(); 
  v.copy(v.random); 
}); 
 
var boxMat = new THREE.PointsMaterial({ 
  size: 0.1, 
  color: "red" 
}); 
 
var box = new THREE.Points(boxGeom, boxMat); 
scene.add(box); 
 
var speed = 2; // единиц в секунду 
var longestDist = 0; 
boxGeom.vertices.forEach(v => { 
  longestDist = Math.max(longestDist, v.dist); 
}); 
var fullTime = longestDist / speed; // продолжительность анимации определяется по самой длинной дистанции, так как скорость постоянная 
console.log({ 
  fullTime 
}); 
 
var clock = new THREE.Clock(); 
var delta = 0; 
var globalTime = 0; 
var clampedDirLength = new THREE.Vector3(); 
 
render(); 
 
function render() { 
  let req = requestAnimationFrame(render); 
  delta = clock.getDelta(); 
  globalTime += delta; 
 
  boxGeom.vertices.forEach(v => { 
    clampedDirLength.copy(v.dir).multiplyScalar(globalTime * speed).clampLength(0, v.dist); // clamp the length! 
    v.copy(v.random).add(clampedDirLength); 
  }); 
  boxGeom.verticesNeedUpdate = true; 
  if (globalTime >= fullTime) { // если текущая продолжительность больше или равна заданной, то останавливаем цикл прорисовки или делаем что-то другое 
    //cancelAnimationFrame(req); 
    boxMat.color.set("aqua"); 
  } 
  renderer.render(scene, camera); 
}
body { 
  overflow: hidden; 
  margin: 0; 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/94/three.min.js"></script> 
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>

Референс

READ ALSO
Карусель со скроллбаром

Карусель со скроллбаром

Подскажите, как сделать скроллбар для каруселиЧтобы скролл реагировал когда переключаешь изображения

198
Реальный приоритет инкремента/декремента

Реальный приоритет инкремента/декремента

Собственно вот в чём вопрос: если верить https://developermozilla

142
Расположение команд в группе

Расположение команд в группе

Задача программы - сортировать команды в группы так, чтобы в одной группе не было команд с одной и той же страныПроблема заключается в следующем:...

216
Поиск значения в массиве объектов и вывод его порядкового номера с ключом

Поиск значения в массиве объектов и вывод его порядкового номера с ключом

Дан массив объектовНайти введенное значение в массиве и выести порядковый номер элемента массива с ключом, в котором оно записано

169