[SOLVED] Move items around a sphere

Hello…

I have a bunch of elements that sit on a sphere…

I need to animate these items to a new position on the sphere.

At the moment i am animating the position values, from a to b… alas the tween is not aware that it needs to animate around a sphere.

Code thus far:

Click the toggle button to see the issue.

I’m sure there’s some special function I can use to adjust/normalize the values… alas “hello wall”!!

any help, greatly appreciated

Instead of tweening the positions, you can tween the quaternions.
A rough concept. I’m using the tweening library from the Three.js distributive, not from GSAP.
https://jsfiddle.net/prisoner849/b0u25h7c/

var radius = 1;
var sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, 32, 24), new THREE.MeshBasicMaterial({color: "gray", wireframe: true}));
scene.add(sphere);

var item = new THREE.Mesh(new THREE.BoxGeometry(0.0625, 0.0625, 0.5), new THREE.MeshBasicMaterial({color: "red", wireframe: true}));
item.geometry.translate(0, 0, 0.25 + radius);
scene.add(item);

var startVector = new THREE.Vector3(0, 0, 1);
var endVector = new THREE.Vector3(
  THREE.Math.randFloat(-1, 1),
  THREE.Math.randFloat(-1, 1),
  THREE.Math.randFloat(-1, 1)
);

var q = new THREE.Quaternion();
function setInitials(){
  q.setFromUnitVectors(startVector.normalize(), endVector.normalize());
  startVector = endVector;
  endVector = new THREE.Vector3(
    THREE.Math.randFloat(-1, 1),
    THREE.Math.randFloat(-1, 1),
    THREE.Math.randFloat(-1, 1)
  );
}
setInitials();

btnMove.addEventListener("click", function(){
  var tween = new TWEEN.Tween(item.quaternion).to(q, 1000).delay(500).onComplete(setInitials);
  tween.chain(tween);
  tween.start();
}, false);
1 Like

The other approach is to use the THREE.Spherical() object and .setFromSpherical() method of THREE.Vector3() object to set start and end points on the sphere, then find the normal of the triangle (start, center, end) and the angle between the start and end vectors, then rotate the start vector around the normal, whilst tweening the angle.

https://jsfiddle.net/prisoner849/b0sxvcsg/

var radius = 1;
var segmentsWidth = 16;
var segmentsHeight = 12;
var sphere = new THREE.Mesh(new THREE.SphereGeometry(radius, segmentsWidth, segmentsHeight), new THREE.MeshBasicMaterial({color: 0x404040, wireframe: true}));
scene.add(sphere);

var item = new THREE.Mesh(new THREE.BoxGeometry(0.0625, 0.0625, 0.5), new THREE.MeshBasicMaterial({color: "aqua", wireframe: true}));
item.geometry.translate(0, 0, 0.25);
scene.add(item);

var sphericalStart = new THREE.Spherical();
var sphericalEnd = new THREE.Spherical(radius, Math.PI * 0.5, 0);
var center = new THREE.Vector3();
var vectorStart = new THREE.Vector3();
var vectorEnd = new THREE.Vector3();

var angle = {value : 0};
var angleEnd = {value: 0};
var normal = new THREE.Vector3();
var lookAt = new THREE.Vector3();
function setInitials(){
  sphericalStart.copy(sphericalEnd);
  sphericalEnd.set(radius,THREE.Math.randFloat(0, Math.PI), THREE.Math.randFloat(0, Math.PI * 2));
  vectorStart.setFromSpherical(sphericalStart);
  vectorEnd.setFromSpherical(sphericalEnd);
  normal.copy(vectorStart).cross(vectorEnd).normalize();
  angle.value = 0;
  angleEnd.value = vectorStart.angleTo(vectorEnd);
}
setInitials();


btnMove.addEventListener("click", function(){
  var tween = new TWEEN.Tween(angle).to(angleEnd, 1000).delay(500).onUpdate(
    function(){
      item.position.copy(vectorStart).applyAxisAngle(normal, angle.value);
      item.lookAt(lookAt.copy(item.position).normalize().multiplyScalar(radius + 1));
    }
  ).onComplete(setInitials);
  tween.chain(tween);
  tween.start();
}, false);
1 Like

Thanks for your reply…

I took a fork of your fiddle and replaced the tween library with gsap.
https://jsfiddle.net/atuwe6wq/4/

Works, as expected.

However… In my code base… I have a bunch of items that need to move to a specific location. I’m struggling to work out what my start/end vectors should be.

I’ve tried using the current items position and the target position… but alas no joy.

const { x, y, z } = point.position
const { x: x2, y: y2, z: z2 } = this.latLngCords[index]

let startVector = new THREE.Vector3(x, y, z)
let endVector = new THREE.Vector3(x2, y2, z2)

const q = new THREE.Quaternion()
q.setFromUnitVectors(startVector.normalize(), endVector.normalize())

TweenMax.to(point.quaternion, 5.5, {
	...q,
	onComplete: () => {
		q.setFromUnitVectors(startVector.normalize(), endVector.normalize())
	}
})

Any thoughts…

I’ve not had a proper play with your other suggestion… Run out of time today, so i’ll take a look again tomorrow… The first solution certainly looks easier.

Thanks again

I would prefer to use the second approach. It looks more complicated than the frist one, but actually it’s not like that :slight_smile:

The first one is just a very rough concept which uses quaternions for geometries whose centers conicide with the center of the sphere (notice, I translate vertices of the box geometry at the value of radius). In your case, centers of boxes don’t coincide with the center of the sphere, they are on the surface of the sphere.

The second one, from my point of view, has correct movement of the object on the sphere. Straight from one point to another, along the surface of the sphere.

Using the second approach, your animation will be like that:

this.local = points.map((point, index) => {
  const { x, y, z } = point.position;
  const { x: x2, y: y2, z: z2 } = this.latLngCords[index];

  let startVector = new THREE.Vector3(x, y, z);
  let endVector = new THREE.Vector3(x2, y2, z2);
  let angle = { value: 0 };
  let angleEnd = startVector.angleTo(endVector);
  let normal = startVector.clone().cross(endVector).normalize();

  TweenMax.to(angle, 5.5, {
    value: angleEnd,
    onUpdate: () => {
      point.position.copy(startVector).applyAxisAngle(normal, angle.value);
      point.lookAt(this.center);
    }
  });
});
3 Likes

Amazing… You have my nomination for man of the year… It works!

Thanks a million

You’re welcome :beers: :wink:

Off topic question… Can you recommend any decent resource for learning this stuff? I’ve seen a few books on amazon with fairly mixed reviews.

Ta

As always, official examples and documentation are my best friends :slight_smile:

A very good book for all 3D Math related stuff is:

I’ve read the first and second edition and still use it as a reference book. Many concepts elaborated in this thread are not three.js related. For example THREE.Spherical's are just spherical/polar coordinates. Topics like that are discussed in the book from scratch.

2 Likes

If it already exists in Three.js, why not to use it :slight_smile:
Of course, everyone’s free to invent his own bicycle :smile:

BTW: I’ve used the following fiddle for a recent stackoverflow post. It’s based on your version of THREE.Spherical but avoids some unnecessary calculations.

https://jsfiddle.net/f2Lommf5/4160/

1 Like

@Mugen87
Bumped into this SO question

Just thought about the approach with tweening of spherical coordinates. It has confusing behaviour when theta changes, for examples, from 355 to 5 degrees. In this case an object moves along not the shortest path (as a user would expect) and makes almost a full turn:
https://jsfiddle.net/prisoner849/6ery1qct/

Whereas the approach with tweening the angle around a triangle’s normal makes an object to move along the shortest path (most expectable behaviour):
https://jsfiddle.net/prisoner849/zcv81gL5/

2 Likes

True. From this point of view, the second approach is better.

1 Like