SyncManager fails to sync zoom

Hi All,

I have a split screen setup and working except for Zoom on the cloned camera. When I started each viewer, I set

sceneMode: SceneMode.SCENE2D

The code in SyncManager.js seems to work for cloning the position, directions and up vectors like so:

function onPrerenderListener(syncManager, scene, time) {

var masterCamera = syncManager._viewer.scene.camera,

nSyncedViewers = syncManager._syncedViewers.length,

syncedViewers = syncManager._syncedViewers,

cesiumClone = Cartesian3.clone,

slaveViewer,

slaveCamera;

while (nSyncedViewers–) {

slaveViewer = syncedViewers[nSyncedViewers],

slaveCamera = slaveViewer.camera;

if (masterCamera == slaveCamera) {

continue;

}

cesiumClone(masterCamera.position, slaveCamera.position);

cesiumClone(masterCamera.direction, slaveCamera.direction);

cesiumClone(masterCamera.up, slaveCamera.up);

slaveCamera.lookAtTransform(masterCamera.transform); // this fixes the slaved view transform…

}

I’m assuming in the scene2D view position.x = left right, y=up down, z = in out (maybe this is a false assumption)

The slaved view seems to be cloning the z value for the x value, as the slaved view moves left/right when I zoom in/out on the masterview.

Perhaps the position in this mode is just a 2D vector, and something else controls zoom?

I have been through all the properties of Camera, and looked at ScreenSpaceCameraController.js… Nothing seems to be relevant.

Any ideas why this is happening?

Thanks in advance.

I dug a little deeper and stepped through the code, in Viewer.js there is a function updateZoomTarget(viewer) called on Viewer.prototype._postRender()

Since I have 2 viewers the viewer._container shows 2 different values when called, one then the other. BUT viewer._zoomTarget is ALWAYS undefined. so updateZoomTarget() just returns immediately, here’s the function:

function updateZoomTarget(viewer) {

var entities = viewer._zoomTarget;

if (!defined(entities) || viewer.scene.mode === SceneMode.MORPHING) {

return;

}

There are other functions in Viewer.js that seem to be related to zoom… Viewer.zoomTo() which calls zoomToOrFly()

_zoomTarget seems to get set in zoomToOrFly(), but zoomTo or zoomToOrFly() aren’t called when I execute in the Chrome debugger.

I don’t understand what the Viewer.zoomTo() function is for, and why it’s not called.

in the Viewer() constructor (Main Function) there is a cesiumWidget.screenSpaceEventHandler set up to call pickAndTrackObject() which calls this.zoomTo() so the zoomTarget should be set by this eventHandler… BUT

THE cesiumWidget doesn’t seem to be setup to handle a ScreenSpaceEventType.WHEEL for zoom… just .LEFT_CLICK and DOUBLE_CLICK here’s the callbacks setup.

cesiumWidget.screenSpaceEventHandler.setInputAction(pickAndSelectObject, ScreenSpaceEventType.LEFT_CLICK);

cesiumWidget.screenSpaceEventHandler.setInputAction(pickAndTrackObject, ScreenSpaceEventType.LEFT_DOUBLE_CLICK);

I am surmizing by the callback function names I may be on the wrong track because these callbacks seem to be for Object Selection and Object Tracking. I’m just trying to get the zoom to sync between 2 Viewers.

I am thinking maybe _zoomTarget isn’t related to the MOUSE WHEEL zoom event , but where do I look for zoom from WHEEL events?

The WHEEL event is processed, just NOT THE SAME in both viewers. The FOCUS viewer handles zoom properly, the UN FOCUSED view that should be slaved, just pans randomly instead of zooming… although at certain conditions this is not consistent… but mostly the SLAVE view is just panning on a diagonal, not zooming.

At first I was thinking perhaps the camera position coordinates were mapped or processed wrong instead of Z for zooming, it was changing X or Y or both? This still may be the case, but I’m not sure where to look… perhaps in the SyncManager().

that will be my next drill down…

this post is cathartic if not informative… but if anyone has a suggestion, I’m open.

thanks…

Jay, SyncManager.js is not part of the Cesium codebase, so I assume you inherited that code from someone on your end who wrote it. From what I can tell, your approach to synchronizing is flawed. You shouldn’t be directly cloning camera properties and should instead use camera.setView in the “slave” views with inputs retrieved from the master view. The zoom code in Viewer.js is only involved with zooming to or tracking an Entity and is a red herring for your purposes. Here’s a simple example of how I would go about syncing two views. This should work in all scene modes. Hopefully you can adapt it for your purposes.

var slaveViewer = new Cesium.Viewer(‘cesiumContainerTop’, {

sceneMode : Cesium.SceneMode.SCENE2D

});

var masterViewer = new Cesium.Viewer(‘cesiumContainerBottom’);

var masterCamera = masterViewer.scene.camera;

var slaveCamera = slaveViewer.scene.camera;

slaveViewer.scene.preRender.addEventListener(function(){

if(slaveViewer.scene.mode !== Cesium.SceneMode.MORPHING){

slaveCamera.setView({

position : masterCamera.positionWC,

heading : masterCamera.heading,

pitch : masterCamera.pitch,

roll : masterCamera.roll

});

}

});

Thanks for your response Matthew.

I did inherit SyncManager.js, as I just found out it wasn’t part of the Cesium codebase. I did get my code working, but I am looking at your suggestion.

I tried using slaveCamera.setView() and got some weird camera lock, where the mouse movement was fighting something that reset the view to max zoom out… I haven’t traced that back yet.

What I found that works is this: I set up a listener callback called onPreRender, In that callback, I set the slaveCamera to masterCamera settings, like this:

slaveCamera.position = masterCamera.position;

slaveCamera.direction = masterCamera.direction;

slaveCamera.up = masterCamera.up;

I discovered that I also need to set the frustum in Scene2D mode. this is non intuitive. In Scene3D mode the position works to handle zoom, but in Scene2D mode, I surmise it’s an orthogonal view with no real 3D camera, to the zoom is connected to the the frustum somehow… therefore the following makes the ZOOM work in scene2D mode:

slaveCamera.frustum = masterCamera.frustum;

I also discovered that the slaveCamera has a distortion when rotating UNLESS you set the lookAtTransform to the masterCamera.transform, like this:

slaveCamera.lookAtTransform(masterCamera.transform);

you can’t just set the property as I did the others because camera.transform is read only

this NOW works for a scene2D view with translation, rotation and zoom, via the normal mouse/controller events…

I am essentially doing as you suggest except not using setView. I may look harder at the event structure to see if setView is called more than once to make it seem like it’s fighting itself. Right now at least I have a working solution, even though I’m not certain it’s the best solution.

Thanks for your input.

Hi Matthew,

I have 2 questions…

1.)

I update the slaveCamera values from the MasterCamera like this:

slaveCamera.position = masterCamera.position;

slaveCamera.direction = masterCamera.direction;

slaveCamera.up = masterCamera.up;

slaveCamera.frustum = masterCamera.frustum;

My predecessor used Cartesian3.clone(slaveCamera.position,masterCamera.position);

I’m not sure what the difference is, (clone will create a new instance?)… BUT, if I use clone to copy the frustum value, it fails.

I don’t get a program error, but the slave view doesn’t zoom properly (scene2D mode)

I’m not sure why you’d want to clone the values? Do you know a reason why?

Also,

2.)

I need to update 2 cursors for the 2 scene views we are using. Currently the SyncManager creates a BillBoard for each cursor, but this is WorldSpace coordinates. I’m thinking it’s better, faster and easier to use ScreenSpace coordinates. However I’m having difficulty finding the right object type for a ScreenSpace controlled graphic. There is an example that does it with html, CSS and google earth code, but I’m using just javascript.

Any suggestions?

Thanks.

Hello Jay,

It’s a good idea to use Cartesian3.clone for the camera position because it will copy the x, y and z values to the position of the second camera. If you set them equal, both camera’s will be using the same Cartesian3 object, and that could have unexpected side effects. You can’t clone the frustum with Cartesian3.clone because the frustum isn’t a Cartesian3 type.

If you’re using two different scene modes, you really should do what Matt suggested above and use camera.setView. 3D and 2D use slightly different coordinate systems, so copying position/direction/up directly won’t have good results.

For your second question, if you want an image that is fixed to a position on the globe, you will need to use a Billboard in world space coordinates. The google earth example shows how to create screen overlays, if you just want to display an image in a fixed position on top of the globe.

Best,

Hannah

Thanks for your input Hannah;

2 questions… I haven’t used Javascript a lot, I am a long time C#, C++ programmer. In these languages, unless you use a reference or copy the pointer from one object to another, the data is copied, not the pointer when I use something like:

x=y;

Why is slaveCamera.position = masterCamera.position, copying by reference rather than value?

Also,

I want the Cursors in 2 views to sync. I want them to operate in Screen space NOT World Space, so billboards won’t work. I know Google Earth has an example of sprites? in screen space, but the code is nothing like the Javascript only code I am using. It seems to use an object called ScreenOverlay, which is not a Cesium object right?

Thanks

Jay

Hi Jay,

Yes, Javascript works differently than C-like languages, and that takes some getting used to. Take this example:

var foo = function() { this.x = 3; this.y = 4};
var a = new foo() //a.x = 3, a.y = 4
b = a;
b.x = 4;
console.log(a); //a.x = 4

``

This is why you want to clone your objects. Anything that isn’t a primitive type (number, string, etc) is shallow copied.

If you’re just getting started with Javascript, I recommend reading this book: http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742

It gives you a good idea of best practice with the language.

As for the screen overlays, since Cesium is web based we don’t need a special obejct to add an overlay. Instead, we can do so entirely in HMTL. We can create new HTML DOM elements and position them on top of the canvas where needed using CSS. In our example of screen overlays, var img = document.createElement(‘img’); creates a new element, and viewer.container.appendChild(img) adds it to the

that also contains the where Cesium is displayed. The only function in Cesium that might be helpful is this one: http://cesiumjs.org/Cesium/Build/Documentation/SceneTransforms.html#.wgs84ToWindowCoordinates

That allows you to convert a position in world space to screen space if you need to do so for positioning your image.

Hope this helps!

Hannah

I don’t want to convert World Coordinates to Screen Coordinates, because of rounding errors. (it seems)

I want to use mouse screen coordinates to place the cursor in 2 synced viewer windows. ScreenSpaceEvenHandler.js has listeners for mousemove etc. I’m ltrying to understand the best way to use the handleMouseMove event (or make my own listener event) to move 2 cursors in sync in SCREEN SPACE not world space.

My app is working now in World Space, and works poorly. The slaved cursor updates late and very inaccurately, sometimes even getting lost… Is this a typical problem with browser based highly event driven Javascript apps? Or do you think it is coded badly?

I think the problem lied in that it is very ineffecient, if mouse XY is projected into World Space to Create World Space XYZ then copied to the SlavedCursor, and then the Slaved Curson XYZ is Projected back onto that screen in it’s screen space… unnecessary… a simple XY cloning should work…

Accuracy is very important in our app, and I am thinking that screen space sync of cursors, will work better,and faster, then we need to make sure the images are registered and rendered properly in 2D or 3D world space.

thanks

thanks.

Hmm, there are some situations where the mouse position can’t be converted to a world position, like when the mouse is over space instead of the globe.
I’m not sure if there’s a better way to get the screen space position in one map projection and relate it to a screen space position in a second map projection without converting it to world space in between.

It’s hard for me to say where the errors are coming from without looking at your code, but it seems like you have the correct approach.

-Hannah