I'm trying to draw few polygons that dynamically change their position.
The problem is that by using:
onUpdatePolygonData(data){
/// DO SOME CALCULATIONS TO GET point1, point2 point3...
if(Cesium.defined(entity.polygon)
entity.polygon.hierarchy = new Cesium.PolygonHierarchy([point1, point2, point3])
else entity.polygon = viewer.entities.add({
polygon = new Cesium.PolygonGraphics({
hierarchy: new Cesium.PolygonHierarchy([point1, point2, point3])
})
});
}
The result is disturbing blinking on the map.
Updating location occurs roughly once in 40ms though seems like frequency of updates have nothing to do with it.
Anyone knows a way to make the changes smoother?
Thanks for help,
David.
You need to use Callbackproperty, it executed every frame so it won't blink and be smoother.
In typescript you can write:
let poly = viewer.entities.add({
polygon: {
hierarchy: new CallbackProperty (() => {
return new PolygonHierarchy([point1, point2,
point3]) ;
}, false)
});
Related
I have a tilemap that only 1 layer is correctly colliding with the player. Been through all the examples, but i can't seem to get it working on multiple layers.
I have 1 tilemap that contains all the json data for a total of 13 layers, but for the example i have only included 3.
I would like for the player to collide with different layers and have different callbacks, e.g. cannot walk through, pick up item if within range etc. but all using 1 spritemap/tilemap.
var game = new Phaser.Game(1200, 780, Phaser.CANVAS, 'phaser-example', { preload: preload, create: create, update: update, render: render });
function preload() {
this.load.tilemap('main_map', 'img here', null, Phaser.Tilemap.TILED_JSON);
this.load.image('sprite_map', 'img here');
this.load.image('player_image', 'img here');
}
var map;
var tileset;
var bLayer;
var wLayer;
var player;
var sLayer;
var cursors;
function create() {
game.physics.startSystem(Phaser.Physics.ARCADE);
// initiallize the tilemap
map = game.add.tilemap('main_map');
map.addTilesetImage('otherNew', 'sprite_map');
//draw the layers
bLayer = map.createLayer(0);
wLayer = map.createLayer(1);
sLayer = map.createLayer(2);
wLayer.resizeWorld();
player = game.add.sprite(600, 600, 'player_image');
game.physics.arcade.enable(player);
player.body.collideWorldBounds = true; // works
//game camera and movment keys here
}
function update() {
game.physics.arcade.collide(player, wLayer); // DOES NOT WORK
game.physics.arcade.collide(player, sLayer); // THIS WORKS
map.setCollision(1, true, wLayer); // DOES NOT WORK
map.setCollision(2, true, sLayer); // THIS WORKS
//movement here already works so didn't include
}
I'm thinking that your wLayer.resizeWorld(); function makes your tilemap's width/height the size of the world. Looks like this is the only difference between wLayer and sLayer.
Setting width/height in Phaser does not automatically resize the collision body. To do that, use the setSize function.
To view your current body, use Phaser's debug methods
this.game.debug.body(someSprite, 'rgba(255,0,0,0.5)');
The answer is that each layer has its own set of tilemaps, and as i was using a spritesheet to generate each layer, those layers had some value that needed to be set. I didn't have the patience to check what actual collision layer I needed, but as I had split them all up into separate logical layers I just set between 0, 100 for each.
// In the create section
map.setCollisionBetween(0, 100,true, wLayer,true);
map.setCollisionBetween(0, 100,true, sLayer,true);
We are working on a school project where the aim is to create a map in which based on the amount of zoom the layer switches from one aggregate level to a smaller aggregate level. Additionally, we have several groups of layers based on a theme for which this needs to apply. So you'd click on a theme and a new group of layers that switches based on zoom level become active and when you click another theme another group of layers become active and switch based on zoom level. This means that the themes are exclusionary, ideally you can't have more than one theme active at a time.
We tried to make this work in several ways already but without much success. Using the L.Control.Layers we were unable to group different layers together under one radio button and have them switch based on zoom since the layer control build into leaflet always splits them up into separate ones. Even using L.layerGroup to combine several layer variables or creating several layers into one variable and then adding them to the map using l.control.layer.
We also tried to use L.easyButton (https://github.com/CliffCloud/Leaflet.EasyButton). This allowed us to put the variables under one button and add a zoom based layer switching inside of it. However, the issue here is that we are unable to deactivate the functionality once activated. Which results in several of them being active at one point and overlapping each other.
If possible we would like to know if we should use a different approach or if either the leaflet control function or the use of easyButton could work and how?
This is example code for one of the buttons, which would appear several times but show a different theme:
L.easyButton( '<span class="star">★</span>', function (polygon) {
var ejerlav_polygon = new L.tileLayer.betterWms(
'http://[IP]:[PORT]/geoserver/prlayer/wms', {
layers: 'prlayer:ejerlav',
transparent: true,
styles: 'polygon',
format: 'image/png'});
var municipality_polygon = new L.tileLayer.betterWms(
'http://[IP]:[PORT]/geoserver/prlayer/wms', {
layers: 'prlayer:municipality',
transparent: true,
styles: 'polygon',
format: 'image/png'});
map.on("zoomend", function() {
if (map.getZoom() <= 10 && map.getZoom() >= 2) {
map.addLayer(municipality_polygon);
} else if (map.getZoom() > 10 || map.getZoom() < 2) {
map.removeLayer(municipality_polygon);
}
});
map.on("zoomend", function() {
if (map.getZoom() <= 11 && map.getZoom() >= 11) {
map.addLayer(ejerlav_polygon);
} else if (map.getZoom() > 11 || map.getZoom() < 11) {
map.removeLayer(ejerlav_polygon);
}
});
}).addTo(map);
If my understanding is correct, you would like to give the user the ability to switch between "themes" (some sort of group of layers that switch themselves based on the map current zoom level), possibly using Leaflet Layers Control?
And regarding the switch based on map zoom, you cannot just change the Tile Layer template URL because you use some WMS?
As for the latter functionality (switching layers within a group / theme based on map zoom), a "simple" solution would be to create your own type of layer that will listen to map "zoomend" event and change the Tile Layer WMS accordingly.
L.LayerSwitchByZoom = L.Class.extend({
initialize: function (layersArray) {
var self = this;
this._layersByZoom = layersArray;
this._maxZoom = layersArray.length - 1;
this._switchByZoomReferenced = function () {
self._switchByZoom();
};
},
onAdd: function (map) {
this._map = map;
map.on("zoomend", this._switchByZoomReferenced);
this._switchByZoom();
},
onRemove: function (map) {
map.off("zoomend", this._switchByZoomReferenced);
this._removeCurrentLayer();
this._map = null;
},
addTo: function (map) {
map.addLayer(this);
return this;
},
_switchByZoom: function () {
var map = this._map,
z = Math.min(map.getZoom(), this._maxZoom);
this._removeCurrentLayer();
this._currentLayer = this._layersByZoom[z];
map.addLayer(this._currentLayer);
},
_removeCurrentLayer: function () {
if (this._currentLayer) {
map.removeLayer(this._currentLayer);
}
}
});
You would then instantiate that layer "theme" / group by specifying an array of layers (your Tile Layers WMS), where the array index corresponds to the zoom level at which that Tile Layer should appear.
var myLayerSwitchByZoomA = new L.LayerSwitchByZoom([
osmMapnik, // zoom 0, osmMapnik is a Tile Layer or any other layer
osmDE, // zoom 1
osmFR, // zoom 2
osmHOT // zoom 3, etc.
]);
Once this new layer type is set, you can use it in the Layers Control like any other type of Layer / Tile Layer, etc.
L.control.layers({
"OpenStreetMap": myLayerSwitchByZoomA,
"ThunderForest": myLayerSwitchByZoomB
}).addTo(map);
Demo: http://jsfiddle.net/ve2huzxw/85/
Note that you could further improve the implementation of L.LayerSwitchByZoom to avoid flickering when changing the layer after zoom end, etc.
Good Day all,
I've been working with Cesium for a bit now and I started when Primitive Collections were the thing to use. I had click and drag primitive rendering working, but now I want to upgrade Cesium and move on to entities. I moved over the code, refactored, and can click and drag to draw shapes; however, before I was able to flip the asynchronous flag and it would render as I moved the mouse. Now, I'm unable to do that. I tried setting 'allowDataSourcesToSuspendAnimation' on the viewer to false, but to no avail. Any help would be extremely appreciated.
In my naivety I forgot to add a code snippet to my question. This is in a clock tick event listener that only fires when mouse down is happening(Boolean value set to true)
var radius = Cesium.Cartesian3.distance(cartesianStartMousePosition, cartesianMousePosition);
if (radius > 0) {
if (currentEntity && currentEntity.id) {
currentEntity.position = cartesianStartMousePosition;
currentEntity.ellipse = {
semiMinorAxis: radius,
semiMajorAxis: radius,
material: new Cesium.ColorMaterialProperty(myColor)
};
currentEntity.label = {
text: 'New Overlay',
scale: 0.35
};
overlayEntities.resumeEvents();
}
else {
currentEntity = new Cesium.Entity({
position: cartesianStartMousePosition,
ellipse: {
semiMinorAxis: radius,
semiMajorAxis: radius,
material: new Cesium.ColorMaterialProperty(myColor)
},
label: {
text: 'New Overlay',
scale: 0.35
},
isSaved: false
});
overlayEntities.add(currentEntity);
}
bDrewPrim = true;
}
It looks to me like you're doing too much work to update the entity. You only need to set the values that have changed, and you should only do that if the change was substantial enough to warrant a graphics update. Try replacing the top half of your if statement with something like this:
var lastRadius = 0;
...
if (radius > 0 && !Cesium.Math.equalsEpsilon(radius, lastRadius, Cesium.Math.EPSILON2)) {
lastRadius = radius;
if (currentEntity && currentEntity.id) {
currentEntity.ellipse.semiMinorAxis = radius;
currentEntity.ellipse.semiMajorAxis = radius;
} else {
// Same as before...
I believe the ellipsoid primitive is being built on a worker thread, so this code tries to avoid setting the new radius every tick unless a real change has been applied to it.
Also, you don't show your mouse down handler, but make sure that you're setting this flag, if you aren't already setting it:
viewer.scene.screenSpaceCameraController.enableInputs = false;
This stops the globe from spinning while you drag-select the ellipse. You can reset this to true on mouse up.
I'm loading a 3D asset into a Away3D scene and I'd like to move the position of the bones in code.
The asset loading all goes well, I grab a pointer to the Mesh and Skeleton while loading:
private function onAssetComplete(evt:AssetEvent):void
{
if(evt.asset.assetType == AssetType.SKELETON){
_skeleton = evt.asset as Skeleton;
} else if (evt.asset.assetType == AssetType.MESH) {
_mesh = evt.asset as Mesh;
}
}
After the asset(s) have finished loading, I have a valid Skeleton and Mesh instance, the model is also visible in my scene. The next thing I tried is the following.
// create a matrix with the desired joint (bone) position
var pos:Matrix3D = new Matrix3D();
pos.position = new Vector3D(60, 0, 0);
pos.invert();
// get the joint I'd like to modifiy. The bone is named "left"
var joint:SkeletonJoint = _skeleton.jointFromName("left");
// assign joint position
joint.inverseBindPose = pos.rawData;
This code runs without error, but the new position isn't being applied to the visible geometry, eg. the position of the bone doesn't change at all.
Is there an additional step I'm missing here? Do I have to re-assign the skeleton to the Mesh somehow? Or do I have to explicitly tell the mesh that the bone positions have changed?
This might not be the best way to solve this, but here's what I figured out:
Away3D only applies joint transformations to the geometry when an animation is present. In order to apply your transforms, your geometry must have an animation or you'll have to create an animation in code. Here's how you do that (preferably in your LoaderEvent.RESOURCE_COMPLETE handler method:
// create a new pose for the skeleton
var rootPose:SkeletonPose = new SkeletonPose();
// add all the joints to the pose
// the _skeleton member is being assigned during the loading phase where you
// look for AssetType.SKELETON inside a AssetEvent.ASSET_COMPLETE listener
for each(var joint:SkeletonJoint in _skeleton.joints){
var m:Matrix3D = new Matrix3D(joint.inverseBindPose);
m.invert();
var p:JointPose = new JointPose();
p.translation = m.transformVector(p.translation);
p.orientation.fromMatrix(m);
rootPose.jointPoses.push(p);
}
// create idle animation clip by adding the root pose twice
var clip:SkeletonClipNode = new SkeletonClipNode();
clip.addFrame(rootPose, 1000);
clip.addFrame(rootPose, 1000);
clip.name = "idle";
// build animation set
var animSet:SkeletonAnimationSet = new SkeletonAnimationSet(3);
animSet.addAnimation(clip);
// setup animator with set and skeleton
var animator:SkeletonAnimator = new SkeletonAnimator(animSet, _skeleton);
// assign the newly created animator to your Mesh.
// This example assumes that you grabbed the pointer to _myMesh during the
// asset loading stage (by looking for AssetType.MESH)
_myMesh.animator = animator;
// run the animation
animator.play("idle");
// it's best to keep a member that points to your pose for
// further modification
_myPose = rootPose;
After that initialization step, you can modify your joint poses dynamically (you alter the position by modifying the translation property and the rotation by altering the orientation property). Example:
_myPose.jointPoses[2].translation.x = 100;
If you don't know the indices of your joints and rather address bones by name, this should work:
var jointIndex:int = _skeleton.jointIndexFromName("myBoneName");
_myPose.jointPoses[jointIndex].translation.y = 10;
If you use the name-lookup frequently (say every frame) and you have a lot of bones in your model, it's advisable to build a Dictionary where you can look up bone indices by name. The reason for this is that the implementation of jointIndexFromName performs a linear search through all joints which is wasteful if you do this multiple times.
i am using GoogleMaps and i have 2 or more markers and they are draggable.
I want to snap 2 markers if they are near and merge them into 1.
is this possible ?
Can someone give me pointers .. how i can realize that ?
You need to handle the drag event on the GMarker object. The trick is what do you do when you detect that you are near enough to another marker to snap them together. I played around a little with this and thought maybe hiding the currently dragged marker might be a good way to go.
GEvent.addListener(marker, "drag", function(point) {
// iterate over your points and for each otherPoint...
if (near (point, otherPoint))
{
// hide this marker
marker.hide ();
// move nearby marker to indicate merge?
// then delete the dragged marker on the dragend (if it was merged)
}
}
Not an entirely elegant solution, but it might suit your purposes.
Edit: I wondered if you were looking for the code to check nearby points, so I updated my example to do that:
function near (point1, point2)
{
sw = new GLatLng(point2.lat() - 0.005, point2.lng() - 0.005);
ne = new GLatLng(point2.lat() + 0.005, point2.lng() + 0.005);
var bounds = new GLatLngBounds(sw, ne);
if (bounds.contains (point1))
return true;
return false;
}