Checking if primitives contain other primitives

Hello,

Is it possible to check if a Primitive contains another Primitive? For example, in my use case, I want to see if a Polygon or Circle contains a Billboard or Polyline. I’ve come across the contains function for the PrimitiveCollection but it seems this is just a lookup rather than checking for containment. I’ve also come across the IntersectionTests functions, which look useful but I think I would still have to write the logic to implement my use case.

Also thanks for creating this amazing open source library and for the fantastic community around it!

Minh

My recommendation would be to override the contains method in the PrimitiveCollection in your code. Do not directly modify the method in the Core code, unless you want to be specifically maintaining that every release.

Here is an example of how I would do it.
Gist: https://gist.github.com/maikuru/1e26e74545fc1470c690

Codepen: http://codepen.io/maikuru/pen/vOgMOO

Sandcastle (paste in):

var testInstanceId = 'FindME';
var viewer = new Cesium.Viewer('cesiumContainer', { });
var primitives = viewer.scene.primitives;

//Override primitive collection
primitives.contains = function(searchFor) {
    //custom find code
    for (var i = 0; i < this._primitives.length; i++) {
        if (this._primitives[i].geometryInstances.id === searchFor) {
            return true;
        }
    }

    //custom find did not find anything so check original contains
    return Cesium.PrimitiveCollection.prototype.contains.call(this, searchFor);
};

var primTest = new Cesium.Primitive({
    geometryInstances : new Cesium.GeometryInstance({
        id: testInstanceId,
        geometry : Cesium.BoxGeometry.fromDimensions({
            vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT,
            dimensions : new Cesium.Cartesian3(400000.0, 300000.0, 500000.0)
        }),
        modelMatrix : Cesium.Matrix4.multiplyByTranslation(
                Cesium.Transforms.eastNorthUpToFixedFrame(Cesium.Cartesian3.fromDegrees(-105.0, 45.0)),
                new Cesium.Cartesian3(0.0, 0.0, 250000), new Cesium.Matrix4()),
        attributes : {
            color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.RED.withAlpha(0.5))
        }
    }),
    appearance : new Cesium.PerInstanceColorAppearance({
        closed: true
    })
});
primitives.add(new Cesium.Primitive({
    geometryInstances : new Cesium.GeometryInstance({
        geometry : new Cesium.RectangleGeometry({
            rectangle : Cesium.Rectangle.fromDegrees(-100.0, 30.0, -93.0, 37.0),
            height: 100000,
            vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
        }),
        attributes : {
            color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.BLUE)
        }
    }),
    appearance : new Cesium.PerInstanceColorAppearance()
}));
primitives.add(primTest);

var found = primitives.contains(testInstanceId);
console.log('Found by InstanceId: ' + found);

found = primitives.contains('notThere');
console.log('Found by InstanceId: ' + found);

found = primitives.contains(primTest);
console.log('Found by Primitive: ' + found)

``

Hi Mike,

Thanks for the information and example! Does that code test that an primitive geometry actually is contained inside another primitive geometry though? I might be wrong but I think it’s just checking that a PrimitiveCollection contains an ID?

For example in my use case, if I had a rectangle geometry and a billboard, I want to determine if the billboard is physically contained within the rectangle. I eventually came across a ray casting implementation that seems to work well enough (https://github.com/substack/point-in-polygon/blob/master/index.js). In my use case, I only need to test 2 dimensions so I converted lat,lon (or cartesians) to x,y for the point and polygons. Circles were a little trickier but I was able to make a circle work with this approach by creating a polygon from the circle by creating points around the center using the radius.

Anyway, this approach works decently enough (still testing and debugging some edge cases) but I mostly wanted to make sure I wasn’t reinventing the wheel with something Cesium already implemented.

It’s an example of how you could add custom code to a core component of Cesium without risk to breaking Cesium’s core engine or having to continuelly apply your modification whenever you update your Cesium library to the latest build.

You’ll still need to modify this block of code from my example to verify if your geometry is contained within.

if (this._primitives[i].geometryInstances.id === searchFor) {
    return true;
}

If you have trouble modifying the sample just send me over a Sandcastle sample of some of your nested primitives and I’ll see if I can help get to the specific logic worked out.

I would strongly recommend against trying to modify or monkey patch PrimitiveCollection.contains to take geometry containment into account. If you require such functionality, you are better off writing a standalone function that does this for you. Otherwise, you will break any code that expects contains to be a list containment function, which is what it is meant for.

Cesium has no out of the box support for determining if one primitive’s geometry is in another, you would have to implement such functionality yourself. It’s probably not a trivial task, but if you decide to try it, we’d be interested in your results.

To elaborate, Matt is recommending you add a new custom method instead of the override I illustrated.

Which is a much safer since you don’t know how the Cesium Core code is using contains and what input it may be testing against now or in the future…

So instead of

//Override primitive collection
primitives.contains = function(searchFor) {
//custom find code
for (var i = 0; i < this._primitives.length; i++) {
if (this._primitives[i].geometryInstances.id === searchFor) {
return true;
}
}

    //custom find did not find anything so check original contains
    return Cesium.PrimitiveCollection.prototype.contains.call(this, searchFor);
};

``

you would use:

//add new Method to primitive collection
primitives.xyzContainsGeometry = function(searchFor) {
//custom find code
for (var i = 0; i < this._primitives.length; i++) {
if (this._primitives[i].geometryInstances.id === searchFor) {
return true;
}
}
return false;
};

``

replace xyx with a project/team/company acronym.

Yup, copy that. That’s my current approach where I’m using an external/non-core function but I definitely learned a lot from your CodePen and samples so thanks for putting that together! When I get a chance I’ll try to put together a CodePen, Sandcastle, or Github gist of the approach I took to help others (I mostly did what I described below using ray casting).

Posting samples is always welcome and greatly appreciated :slight_smile: