Plane normals calculation help

Hi all,

I apologise if this is worded incorrectly, or the code style of my sample is poor, I am brand new to Cesium and trying to learn the ropes. The code provided can be copy-pasted directly into sandcastle, which should illustrate my problem.

What I am trying to achieve:
I want to create a plane, than can be manipulated (position and normal), which will be used to slice a set of points. Points on 1 side of the plane should be coloured red, while points on the other side should be coloured yellow.

I’m having trouble getting a plane to slice a set of points in the correct orientation. The plane I create seems to be perpendicular to the ground (which would be the correct initial state), however the slicing seems to be happening at an angle. I have attempted to draw a line that Illustrates what is being sliced, however I cannot match the line to the plane. It is critical that the plane can be moved, rotated and angled in any direction, and will slice according to its position and normal direction.

I have created a sample application that can be pasted into sandcastle that demonstrates the problem I am having, and would greatly appreciate any help anyone can provide. I’ve spent close to 3 days trying to solve this problem, so this really is my last resort. I have read a lot of documentation and threads, and my understanding is still quite low, so if possible, I would really appreciate some code snippets rather than “you need to do X” advise.

Bonus points if you can integrate some sliders or controls to manipulate the plain in sandcastle for me to test.

Thanks,

Pete

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

const degrees = [-107.0, 40.0, 300000.0];
const planeNormal = Cesium.Cartesian3.UNIT_Y;
const planeDimensions = [400000.0, 300000.0];

// Calculate the end point of the line in the direction of the plane's normal
const lineLength = 500000.0;
const scaledNormal = Cesium.Cartesian3.multiplyByScalar(planeNormal, lineLength, new Cesium.Cartesian3());
const redPlanePosition = Cesium.Cartesian3.fromDegrees(...degrees);
const lineEndPoint = Cesium.Cartesian3.add(redPlanePosition, scaledNormal, new Cesium.Cartesian3());

function createPlaneFromPositionNormal(position, normal) {
  const distance = Cesium.Cartesian3.dot(normal, position);
  return new Cesium.Plane(normal, -distance);
}

// This is where the slicing is calculated from (i think??) Help!
const redPlane = viewer.entities.add({
  name: "Red plane with black outline",
  position: Cesium.Cartesian3.fromDegrees(...degrees),
  plane: {
    plane: createPlaneFromPositionNormal(Cesium.Cartesian3.fromDegrees(...degrees), planeNormal),
    dimensions: new Cesium.Cartesian2(...planeDimensions),
    material: Cesium.Color.RED.withAlpha(0.5),
    outline: true,
    outlineColor: Cesium.Color.BLACK,
  },
});

// This is where I want the plane to be (but not the correct angle)
const greenPlane = viewer.entities.add({
  name: "Green plane with black outline",
  position: Cesium.Cartesian3.fromDegrees(...degrees),
  plane: {
    plane: new Cesium.Plane(planeNormal, 0.0),
    dimensions: new Cesium.Cartesian2(...planeDimensions),
    material: Cesium.Color.GREEN.withAlpha(0.5),
    outline: true,
    outlineColor: Cesium.Color.BLACK,
  },
});



// Add the line to the viewer
const normalLine = viewer.entities.add({
  name: "Normal Line",
  polyline: {
    positions: [redPlanePosition, lineEndPoint],
    width: 5,
    material: Cesium.Color.BLUE,
  },
});

const pointStyle = {
  pixelSize: 10,
  color: Cesium.Color.YELLOW,
};

const points = [
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-107.0, 41.0, 200000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-107.0, 39.0, 200000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-105.0, 41.0, 200000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-105.0, 39.0, 200000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-107.0, 41.0, 400000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-107.0, 39.0, 400000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-105.0, 41.0, 400000.0), point: pointStyle }),
  viewer.entities.add({ position: Cesium.Cartesian3.fromDegrees(-105.0, 39.0, 400000.0), point: pointStyle }),
];


// All points *in front of* the plane, should be coloured yellow, while ones behind, red.
for (let point of points) {
  const pointPosition = point.position.getValue(viewer.clock.currentTime);
  const redPlanePosition = redPlane.position.getValue(viewer.clock.currentTime);
  const redPlaneEquation = createPlaneFromPositionNormal(redPlanePosition, planeNormal);

  const distance = Cesium.Plane.getPointDistance(redPlaneEquation, pointPosition);

  if (distance <= 0) {
    point.point.color = Cesium.Color.RED;
  }
}

viewer.zoomTo(viewer.entities);

Hi @Pete_S, I think it is important to keep in mind the distinction between a Plane and a plane-shaped Entity.

A plane-shaped Entity is easy to visualize: it’s a flat surface at a given position and orientation. But the Plane is a mathematical object in Hessian Normal form. I find it pretty awkward to think about where a given Plane is located—the distance needs to be relative to the origin of your current working coordinate system. In the case of your set of points, that would be the center of the Earth. Changing the normal would then rotate the plane around the center of the Earth.

To slice your points based on a plane-shaped Entity, I can think of two options:

  1. Construct a mathematical Plane that lines up with your planar Entity, and re-construct it every time the Entity moves. This could be awkward to reason about, as mentioned.
  2. Inverse transform the points from the coordinates of the Entity back to the coordinates of the raw mathematical Plane from which the Entity was constructed. This is easier, since you can derive a current version of the required transform via Entity.computeModelMatrix.

I tried the second option in this Sandcastle example. You can try modifying the value of redPlaneEntityHeading to see how the slicing of the points changes as the planar Entity rotates.

Thank you! I will try and implement what you’ve explained into our production code and see how it works.

To answer one of your questions in the comments of the code, the line was illustrating the angle the plane appeared to be slicing at, rather than the plane entity that was representing where I wanted it to slice at. I can remove the line now.

I appreciate the help, fingers crossed this solves our problem! (we are slicing 200,000+ points in production)

1 Like