The issue lies in the fact that ArrowHelper
utilizes its own unique rotation calculations. It generates a unit arrow pointing upwards in the +Y direction. Then, it applies custom math to establish the orientation to ensure that the line points in the specified direction in local space.
This becomes evident when you create the arrow and examine its rotation:
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, color);
console.log(arrow.rotation.x, arrow.rotation.y, arrow.rotation.z);
You will observe that rotation.z is already configured to rotate the +Y arrow to face +X. Therefore, modifying these rotations results in the arrow no longer being oriented towards +X.
Consequently, manipulating the arrow using arrow.rotation
does not produce the expected outcome.
If you attach the arrow to an Object3D
and then rotate that object, the arrow will behave as anticipated (or at least as I anticipate 😅)
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth/window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene.add(new THREE.GridHelper(10, 10));
function addArrow(x, ry, rz, color) {
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, color);
var ob = new THREE.Object3D();
ob.position.x = x;
ob.rotation.order = 'XYZ';
ob.rotation.y = ry;
ob.rotation.z = rz;
scene.add(ob);
ob.add(arrow);
return ob;
}
addArrow(-4, 0, 0, 0xFF0000);
addArrow(-2, 0, 0.5, 0x00FF00);
addArrow( 0, 0.5, 0.5, 0xFFFF00);
const arrow = addArrow( 2, 0.5, 0.5, 0x00FFFF);
camera.position.z = 6;
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
var animate = function () {
requestAnimationFrame( animate );
arrow.rotation.x += 0.01;
renderer.render( scene, camera );
};
animate();
body { margin: 0; }
canvas { display: block; }
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2e5a465c4b4b6e1e001f1f1b001e">[email protected]</a>/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="ef9b879d8a8aafdfc1dededac1df">[email protected]</a>/examples/js/controls/OrbitControls.js"></script>
My Expectations:
The rotations are relative to the local coordinate system. With the rotation order set to 'XYZ', the full matrix calculation would be:
matrix = projection *
view *
obTranslation *
obXAxisRotation *
obYAxisRotation *
obZAxisRotation *
obScale *
arrowOrientation;
In any scenario:
var origin = new THREE.Vector3(0, 0, 0);
var xDir = new THREE.Vector3(1, 0, 0);
var length = 1;
var arrow = new THREE.ArrowHelper(xDir, origin, length, 0xff0000);
var ob = new THREE.Object3D();
scene.add(ob);
ob.add(arrow);
ob.rotation.order = 'XYZ';
When viewed from 0, 0, 5, this setup results in an arrow pointing to the right.
I prefer to visualize matrices as applied from right to left. Considering the formula above, the scale is applied first. Since it's 1,1,1, there is no change.
Next, the zAxisRotation is applied. With 0.5 radians (approximately 30 degrees), the arrow now points slightly upwards.
Subsequently, the yAxisRotation is applied, causing the slightly tilted arrow to point back into the distance.
Finally, the xAxisRotation is applied, causing this uniquely oriented arrow to spin around the x-axis.
Execute the code and drag on the provided sample to view from above. The result will align with the description provided.
Therefore, you have the choice to either create a +X facing ArrowHelper and attach it to an Object3D or adjust the rotations accordingly, understanding that an ArrowHelper actually creates a +Y arrow initially.