Create Cesium.Ray from Cesium.Cartesian3 and Cesium.HeadingPitchRoll

Viewed 475

Is it possible to create a Cesium.Ray from a Cesium.Cartesian3 and a Cesium.HeadingPitchRoll?

If so, how would one go about this?

EDIT 1:

const hpr = Cesium.HeadingPitchRoll.fromDegrees(0, 0, 0)
const matrix3 = Cesium.Matrix3.fromHeadingPitchRoll(hpr)

// The UNIT_X vector is "forward" in Cesium.  We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
const direction = Cesium.Matrix3.multiplyByVector(
  matrix3,
  Cesium.Cartesian3.UNIT_X,
  new Cesium.Cartesian3()
)

const origin = Cesium.Cartesian3.fromDegrees(i.longitude, i.latitude, i.altitude)

// Now we can construct the Ray.
const ray = new Cesium.Ray(origin, direction)

const finalPoint = Cesium.Ray.getPoint(ray, i.altitude)
const e = map.viewer.entities.add({
  name: i.id,
  polyline: {
    positions: [origin, finalPoint],
    width: 4,
    arcType: Cesium.ArcType.NONE,
    material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED),
  },
})

With HPR set to (0,0,0), this results in:

enter image description here

With a pitch of 0, I would be expecting the ray to be parallel with the horizon instead of what seems to be +45 degrees.

1 Answers

Yes, it is possible. These two kinds of objects hold different kinds of information, so be careful that you're not losing data in the conversion.

  • A Ray has an origin (Cartesian position) and a direction which is just a unit vector, not the same as a full orientation.

  • The HeadingPitchRoll is a full orientation. The Heading and Pitch work together to identify a particular "forward" direction, and Roll indicates a rotation about that direction vector. This Roll rotation does not affect the direction, and so will not have an effect on a Ray.

Here's the code:

// A Cesium Ray consists of an "origin" and a "direction".
// The origin is just a Cartesian3 from anywhere.
var origin = new Cesium.Cartesian3(1.0, 2.0, 3.0);

// For this question, "direction" comes from a HeadingPitchRoll.
// Let's establish an HPR here.
var heading = Cesium.Math.toRadians(0);  // YOUR VALUE HERE
var pitch = Cesium.Math.toRadians(0);    // YOUR VALUE HERE
var roll = Cesium.Math.toRadians(0);     // This one does nothing.

var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);

// Next we need a direction vector from the HPR.  We'll use
// a rotation matrix (3x3) to do the conversion.
var matrix3 = Cesium.Matrix3.fromHeadingPitchRoll(hpr);

// The UNIT_X vector is "forward" in Cesium.  We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
var direction = Cesium.Matrix3.multiplyByVector(matrix3, Cesium.Cartesian3.UNIT_X,
    new Cesium.Cartesian3());

// Now we can construct the Ray.
var ray = new Cesium.Ray(origin, direction);

console.log('Ray: ' + JSON.stringify(ray));

If you run this with the default zero heading/pitch shown here, you'll get the unit X vector back again:

Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":1,"y":0,"z":0}}

But if you set the pitch to 45 degress:

var heading = Cesium.Math.toRadians(0);
var pitch = Cesium.Math.toRadians(45);

You'll see the X direction has pitched up towards +Z.

Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":0.7071067811865476,"y":0,"z":0.7071067811865475}}

A positive heading will rotate the X vector towards -Y (clockwise as seen from +Z above).

var heading = Cesium.Math.toRadians(45);
var pitch = Cesium.Math.toRadians(0);

Ray: {"origin":{"x":1,"y":2,"z":3},"direction":{"x":0.7071067811865476,"y":-0.7071067811865475,"z":0}}


EDIT: The question was revised a bit. The Ray's direction needs to be in Earth-Centered Fixed frame, not local-up frame, for the intended use.

Here's a longer demo that shows how to handle this. You can also view a live demo on Sandcastle.

var viewer = new Cesium.Viewer("cesiumContainer");

const i = {
  id: 'test',
  longitude: -79,
  latitude: 40,
  altitude: 500
};

// Create the origin and hpr.
const origin = Cesium.Cartesian3.fromDegrees(i.longitude, i.latitude, i.altitude)

let hpr = Cesium.HeadingPitchRoll.fromDegrees(0, 0, 0)

// Cesium uses East-North-Up axes, where East is zero.  If you want North == Zero,
// you must subtract 90 degrees' worth of radians (pi/2).
hpr.heading -= Cesium.Math.PI_OVER_TWO

// We'll get the orientation, which is a quaternion.  This is specific to the
// location on the planet.  Also it comes in handy later for the 3D model vis.
const orientation = Cesium.Transforms.headingPitchRollQuaternion(origin, hpr);

// Get a Matrix3 version of that Quaternion.
const matrix3 = Cesium.Matrix3.fromQuaternion(orientation);

// The UNIT_X vector is "forward" in Cesium.  We'll multiply the rotation
// matrix by that unit vector, to get our direction vector.
const direction = Cesium.Matrix3.multiplyByVector(
  matrix3,
  Cesium.Cartesian3.UNIT_X,
  new Cesium.Cartesian3()
)


// Now we can construct the Ray.
const ray = new Cesium.Ray(origin, direction)

const finalPoint = Cesium.Ray.getPoint(ray, i.altitude)
const e = viewer.entities.add({
  name: i.id,
  polyline: {
    positions: [origin, finalPoint],
    width: 4,
    arcType: Cesium.ArcType.NONE,
    material: new Cesium.PolylineArrowMaterialProperty(Cesium.Color.RED),
  },
})

// Optionally, show an aircraft model with this HPR.
const m = viewer.entities.add({
  position: origin,
  orientation: orientation,
  model: {
    uri: "../SampleData/models/CesiumAir/Cesium_Air.glb",
    minimumPixelSize: 64,
  }
})

viewer.zoomTo(e)
Related