Prevent geometries from 'bending' around the globe

To preface, I’m using b27 until 1.0 is released.

I was wondering if there’s a way to prevent geometries from following the curvature of the globe. I’m trying to implement my own hemisphere outline generation using circle outline geometries. The issue I’m running into is that each circle follows the curvature of the Earth, so it ends up looking strange (the bottom-most circle appears to be much ‘lower’ than the lat/lon I have defined as the middle).

Just in case my description isn’t making sense, here’s some sandcastle code to reproduce what I’m seeing. Copy / paste the following into the cesiumjs.org sandcastle, replacing any other code:

require([‘Cesium’], function(Cesium) {

“use strict”;

Cesium.Math.setRandomNumberSeed(1234);

var viewer = new Cesium.Viewer(‘cesiumContainer’);

var scene = viewer.scene;

var primitives = scene.primitives;

var solidWhite = Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.WHITE);

// Combine instances each with a unique color.

// We can combine heterogeneous geometries as we

// do here as long as vertex formats match.

var instances = ;

var height = 0;

var center = Cesium.Cartesian3.fromDegrees(-65.0, 35.0);

var radius = 5000000.0;

var radiusStep = radius / 5;

var rectangle = Cesium.Rectangle.fromDegrees(-67.0, 27.0, -63.0, 32.0);

for (var i = 0; i < 5; ++i) {

height = 200000.0 * i;

instances.push(new Cesium.GeometryInstance({

geometry : new Cesium.CircleOutlineGeometry({

center : center,

radius : (radius - (i * radiusStep)) + 1,

height : height,

vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT

}),

attributes : {

color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.5}))

}

}));

//instances.push(new Cesium.GeometryInstance({

// geometry : new Cesium.RectangleGeometry({

// rectangle : rectangle,

// height : height,

// vertexFormat : Cesium.PerInstanceColorAppearance.VERTEX_FORMAT

// }),

// attributes : {

// color : Cesium.ColorGeometryInstanceAttribute.fromColor(Cesium.Color.fromRandom({alpha : 0.5}))

// }

//}));

}

primitives.add(new Cesium.Primitive({

geometryInstances : instances,

appearance : new Cesium.PerInstanceColorAppearance({

flat : true,

translucent : true,

closed : true

})

}));

Sandcastle.finishedLoading();

});

Is there a way to disable this feature for just one geometry / primitive? In general, I do like it, but in this case it’s actually hindering me.

Thanks,

Joe

Hi,

This should be pretty easy to do with a custom geometry that computes the circle in, for example, the plane z=0. Then set the primitive’s modelMatrix using Transforms.eastNorthUpToFixedFrame to position the center of the circle. You could start with the circle function in this example.

Instead of writing a custom geometry, you could also compute the points and modelMatrix and use the PolylineCollection. That will be a bit easier.

Patrick

That computeCircle with the modelMatrix was exactly what I needed. Thanks!

This might be outside the context of this thread, but somehow different latitude and longitude values are giving me an error stating ‘all attribute lists must have the same number of attributes.’ It’s a rather ambiguous error and doesn’t point to my code (it’s in the rendering loop). The only thing I’m using the lat and lon values for is to center the circle (the values are only used to create Cesium Cartesian3 objects). Do you have any idea how this error could be tied to the lat / lon? I realize that I’m probably not giving enough information, but I don’t have too much else other than that.

I should add, it works for a good number of lat/ lon values, but not all of them.

That error is coming from the actual geometry. Geometries are represented by parallel arrays that store positions, normals and other per vertex information. That error is thrown when the lists don’t represent the same number of vertices.
Can you paste sample code that’s causing this error? It sounds like it might be a bug in the geometry.

-Hannah

There’s probably a bit more here than is necessary, but I’ve included the code I have to actually generate the dome structure. Paste this into cesium’s sandcastle :

function computeCircle(radius, alt) {
    var positions = [];

    for (var i = 0; i <= 360; i++) {
        var radians = Cesium.Math.toRadians(i);
        positions.push (new Cesium.Cartesian3(radius * Math.cos(radians), radius * Math.sin(radians), alt));
    }
   
    return positions;
}

function getSphereOutlineGeometries(cLat, cLon, cAlt, radius, ellipsoid, degreeStep) {
var geoms = ;

    var center = ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(cLon, cLat, cAlt));

    var step = 360;
    var inc = Math.ceil(step / degreeStep);
    step = step / inc;

    var radiusStep = radius / step;
   
    var alts = [];
    var radii = [];

    var alt, heightToTop, geomRad, circle, lineGeom;

    // compute the circles of the dome
    for (var i = 0; i <= 360; i+= inc) {
        alt = cAlt + (radius - ((step - (i / inc)) * radiusStep));
        alts.push (alt);
        heightToTop = (radius - alt) + cAlt;

        // Some sphere math here, a = sqrt(h * (2r - h)), where a is the radius of a smaller circle.  Add 1 for always > 0 radius
        geomRad = Math.sqrt (heightToTop * ((2 * radius) - heightToTop)) + 1;

        radii.push(geomRad);

        circle = computeCircle(geomRad, alt - cAlt);
       
        lineGeom = new Cesium.GeometryInstance({
            geometry : new Cesium.PolylineGeometry({
                positions : circle,
                vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
            }),
            modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center)
        });

        geoms.push(lineGeom);
    }

    // draw fill lines to give more definition to the shape
    for (i = 0; i <= 360; i += step) {
        var positions = [];

        var radians = Cesium.Math.toRadians(i);
     
        for (var j = 0; j < alts.length; j++) {
            positions.push(new Cesium.Cartesian3(radii[j] * Math.cos(radians), radii[j] * Math.sin(radians), alts[j] - cAlt));
        }

        lineGeom = new Cesium.GeometryInstance({
            geometry : new Cesium.PolylineGeometry({
                positions : positions,
                vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
            }),
            modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center)
        });

        geoms.push(lineGeom);

        positions = [];

        positions.push (new Cesium.Cartesian2(0, 0));
        positions.push (new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians)));

        lineGeom = new Cesium.GeometryInstance({
            geometry : new Cesium.PolylineGeometry({
                positions : positions,
                vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
            }),
            modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center)
        });

        geoms.push(lineGeom);
   
    }

   
    return geoms;
}

var widget = new Cesium.CesiumWidget('cesiumContainer');
var scene = widget.scene;
var primitives = scene.primitives;
var ellipsoid = scene.globe.ellipsoid;

var dome = getSphereOutlineGeometries(90, 0, 0, 5000000, ellipsoid, 10);

primitives.add(new Cesium.Primitive({
    geometryInstances : dome,
    appearance : new Cesium.PolylineMaterialAppearance ({
        material : new Cesium.Material ({
            fabric : {
                type : 'Color',
                uniforms : {
                    color : new Cesium.Color(1, 1, 1, 1)
                }
            }
        })
    })
}));

``

You’ll notice in the code that I place the dome right on the North Pole (lat : 90, lon: 0). This will immediately fail some some error. If you, for example, swap the lat and long (the 90 and 0 parameters in the getSphereOutlineGeometries function), you’ll get a properly rendered dome instead of the error. I only use the lat / lon values to define the ‘center’ var, which is then used in the modelMatrix to position the dome. This seems like an error in cesium itself, but I’m not positive.

positions = ;

positions.push (new Cesium.Cartesian2(0, 0));
positions.push (new Cesium.Cartesian2(radius * Math.cos(radians), radius * Math.sin(radians)));

lineGeom = new Cesium.GeometryInstance({
geometry : new Cesium.PolylineGeometry({
positions : positions,
vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT
}),
modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center)
});

This part of your code is invalid. You can’t create a PolylineGeometry with Cartesian2 positions, it has to be Cartesian3.

I’ve had it set both ways and it still throws the error either way.

At a glance, I’m guessing that one of the polylines you’re creating has invalid positions. Since you’re creating so many different polylines in this example, I wasn’t able to narrow it down and find which polyline is causing the problem. If you can write a new example that creates only one PolylineGeometry that causes this error, I can take a closer look at it.

Best,

Hannah

function computeCircle(radius, alt) {

var positions = ;

for (var i = 0; i <= 360; i++) {

var radians = Cesium.Math.toRadians(i);

positions.push (new Cesium.Cartesian3(radius * Math.cos(radians), radius * Math.sin(radians), alt));

}

return positions;

}

function getSphereOutlineGeometries(cLat, cLon, cAlt, radius, ellipsoid, degreeStep) {

var geoms = ;

var center = ellipsoid.cartographicToCartesian(Cesium.Cartographic.fromDegrees(cLon, cLat, cAlt));

var step = 360;

var inc = Math.ceil(step / degreeStep);

step = step / inc;

var radiusStep = radius / step;

var alts = ;

var radii = ;

var alt, heightToTop, geomRad, circle, lineGeom;

// compute the circles of the dome

//for (var i = 0; i <= 360; i+= inc) {

alt = cAlt;

alts.push (alt);

heightToTop = (radius - alt) + cAlt;

// Some sphere math here, a = sqrt(h * (2r - h)), where a is the radius of a smaller circle. Add 1 for always > 0 radius

geomRad = Math.sqrt (heightToTop * ((2 * radius) - heightToTop)) + 1;

radii.push(geomRad);

circle = computeCircle(geomRad, alt - cAlt);

lineGeom = new Cesium.GeometryInstance({

geometry : new Cesium.PolylineGeometry({

positions : circle,

vertexFormat : Cesium.PolylineMaterialAppearance.VERTEX_FORMAT

}),

modelMatrix : Cesium.Transforms.eastNorthUpToFixedFrame(center)

});

geoms.push(lineGeom);

//}

return geoms;

}

var widget = new Cesium.CesiumWidget(‘cesiumContainer’);

var scene = widget.scene;

var primitives = scene.primitives;

var ellipsoid = scene.globe.ellipsoid;

var dome = getSphereOutlineGeometries(90, 0, 0, 5000000, ellipsoid, 10);

primitives.add(new Cesium.Primitive({

geometryInstances : dome,

appearance : new Cesium.PolylineMaterialAppearance ({

material : new Cesium.Material ({

fabric : {

type : ‘Color’,

uniforms : {

color : new Cesium.Color(1, 1, 1, 1)

}

}

})

})

}));

``

This now only creates the initial circle (only one polyline). If you’d like another even more basic example, let me know. The issue only seems to occur when near the prime meridian and potentially the poles (longitude is 180, -180 and lat is 90, -90). I don’t think any of the positions in the array are invalid because the same code will draw a circle just fine when the center point is located away from these problem areas.

I meant the exact opposite of the prime meridian, I don’t think it has a fancy name (just the 180th). I think the issue might have something to do with wrapping the negative values into positive values and vice-versa.

That is called the International Date Line, and it is indeed that is causing the problem. The PolylineGeometry knows to break itself up into pieces when it crosses the IDL, but since you’re using a model matrix to reposition the geometry, a function in the geometry pipeline also attempts to break up the line. However, it misses adding extra positions to some of the other attributes lists that define the geometry, which is causing the error.

I created an issue a few weeks ago when I found the same problem happening with the SimplePolylineGeometry. #1894

This probably won’t be fixed for 1.0, but hopefully we’ll be able to get to it for the following release.

In the meantime, changing your polyline positions so they don’t need the model matrix should fix the problem.

-Hannah

Thanks for the confirmation of the bug and the link to the issue. Do you have any tips on using the latitude / longitude and the radius to manually position the points? It seems a bit more complicated than just calculating a circle of a given radius and then letting the modelMatrix position it and I’m not entirely sure how the math would work.

Thanks,

Joe