I have a path moving over time.
I use Cesium.sampleTerrain to get positions elevation and drape them on the terrain.
The problem is that, even if all points are on the terrain, the line connecting 2 points sometimes goes under the terrain.
How can I do to drape also connecting lines on the terrain?
Here is my code:
var promise = Cesium.sampleTerrain(terrainProvider, 14, positions);
Cesium.when(promise, function(updatedPositions) {
var cartesianPositions = Cesium.Ellipsoid.WGS84.cartographicArrayToCartesianArray(updatedPositions);
var sample = new Cesium.SampledPositionProperty();
sample.setInterpolationOptions({
interpolationDegree : 3,
interpolationAlgorithm : Cesium.HermitePolynomialApproximation
});
$(cartesianPositions).each(function(index, cartPosition) {
var time = Cesium.JulianDate.addSeconds(start, index*10, new Cesium.JulianDate());
sample.addSample(time, cartPosition);
})
var target = viewer.entities.add({
position: sample,
path: {
resolution: 60,
material:Cesium.Color.BLUE,
width: 4,
trailTime: 422*10,
leadTime: 0
}
});
});
So like Matthew says; Cesium doesn't currently support a 'polyline' type entity with draping over terrain.
If you find that the Entity API isn't giving you what you need, it might be worth digging into the lower-level Primitives API to gain finer control - more specifically the GroundPrimitive geometry.
Among others; GroundPrimitives currently support the CorridorGeometry.
I have no experience with temporal data plotting within Cesium, but I would suggest you consider this approach rather than the async promise approach, which (IMO) seems like more of a hack born from the absence of a GroundPrimitive-type solution at the time.
Here's a crude example of a GroundPrimitive in action (note we don't need any z values):
var viewer = new Cesium.Viewer('cesiumContainer');
var corridorInstance = new Cesium.GeometryInstance({
geometry : new Cesium.CorridorGeometry({
vertexFormat : Cesium.VertexFormat.POSITION_ONLY,
positions : Cesium.Cartesian3.fromDegreesArray([
-122.26, 46.15,
-122.12, 46.26,
]),
width : 100
}),
id : 'myCorridor',
attributes : {
color : new Cesium.ColorGeometryInstanceAttribute(0.0, 1.0, 1.0, 0.5)
}
});
var corridorPrimitive = new Cesium.GroundPrimitive({
geometryInstance : corridorInstance
});
viewer.scene.primitives.add(corridorPrimitive);
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(-122.19, 46.20, 10000.0)
});
Which will give you this:
Cesium does not currently support draping lines on terrain, but it is on our road map and really important to us. This is actually an extremely complicated problem to handle correctly in all cases (and is even more complicated because of the limitations of WebGL). It will require a lot of research and experimentation and there's no hard timeline for when it will be finished. We should have a version of it for static lines by spring as part of our 3D Tiles work, but dynamic lines are probably further out.
If you're interested in following development of this feature, keep your eye on issue #2172 in our GitHub repository. We'll also make announcements on our blog/twitter/forum when this feature is part of an official release.
Related
Is there a way to use x,y coordinates stored as attributes of each node to layout a graph using the coordinates that were calculated by another program?
And if not, would it be possible to implement this somehow - how does one get started on this?
Is there a standard way how to provide the node positions to the cytoscape.js web viewer somehow?
Background: it is trivial to use Python networkx to calculate some layouts which are not supported in Cytoscape, and it would also be trivial to store the coordinates as node attributes. All that would then be need is some way for Cytoscape to use those coordinates to find node positions instead of using a layout algorithm.
This is a quite easy thing to do. Many examples on use this functionlity in the demos, as you can see here:
1: FCose Demo
2: Cose Blicent Demo
3: d3-force Demo
4: Euler Compound Demo
5: Spread Demo
As you can see, there are an abundance of examples for this in the demos, but also in the docs. You can see one here and here:
// can use reference to eles later
var eles = cy.add([
{ group: 'nodes', data: { id: 'n0' }, position: { x: 100, y: 100 } },
{ group: 'nodes', data: { id: 'n1' }, position: { x: 200, y: 200 } },
{ group: 'edges', data: { id: 'e0', source: 'n0', target: 'n1' } }
]);
The json used in the .add() function can be created in your js application or directly in Python and added to the graph as some of the examples do.
In general, you should take some time and skim through through the docs. The ability to position nodes via x and y is quite basic and is one of the first pages in the docs.
If you don't understand the description in the docs and the examples I provided, please edit your question and add your current code as a Minimal, Reproducible Example, where you can show your efforts to implement the positioning.
Edit:
As #maxkfranz pointed out, the preset layout plays a big role here. The documentation states this in the Initialisation Chapter:
If you want to specify your node positions yourself in your elements JSON, you can use the preset layout — by default it does not set any positions, leaving your nodes in their current positions (i.e. specified in options.elements at initialisation time).`
I am new to using Cytoscape so things that are "easy" are not so easy for me. I often don't even know the right way to ask a question. Everyone has things that are hard and things that are easy, and step by step we expand our knowledge so what is hard today may be easy tomorrow.
Anyway, here is something that may be a part of the solution you are looking for:
In the Cytoscape desktop application, you can create a "Style" that maps a node attribute to "X Location" and another node attribute to "Y Location".
This is a dart newbie question about how to do "multithreading" in dart.
(Excuse me I am an old java developer ...)
So I have this kind of code (se below) but since recreating the gui is costly I would like to defer it so that instead of recreating the gui in the _onWindowResize() I would like to start a thread that does this when the size has been stable some time. E.g. for one second.
If a thread is already is started do nothing. (Btw, StageXL is cool ....)
(This will also fix the bug that _onWindowResize() is called twice by the dart:html ...)
...
html.window.onResize.listen((e) => _onWindowResize());
}
_createGui() {
var shape = new Shape();
shape.graphics.ellipse(html.window.innerWidth / 2, html.window.innerHeight / 2, html.window.innerWidth / 4, html.window.innerHeight / 4);
shape.graphics.fillColor(Color.Red);
stage.addChild(shape);
}
void _onWindowResize() {
print("New window size ${html.window.innerWidth}x${html.window.innerHeight}");
stage = new Stage('stage', canvas);
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
renderLoop = new RenderLoop();
renderLoop.addStage(stage);
juggler = renderLoop.juggler;
_createGui();
}
One can send work to other threads in Dart via Isolates, but this won't work for your scenario since it's mostly about modifying the UI of the app.
One cannot share objects between isolates in Dart (or using WebWorkers in general). So you cannot pass the canvas into an Isolate to create your stage, renderloop, etc.
If you are doing complex calculations (Physics, for example), it might make sense to send those off to an Isolate and use the result to update the UI.
I'm attempting to load a scene from a file into Three.js (custom format, not one that Three.js supports). This particular format describes a scene graph where each node in the tree has a transform specified as a 4x4 matrix. The process for pushing it into Three.js looks something like this:
// Yeah, this is javascript-like psuedocode
function processNodes(srcNode, parentThreeObj) {
for(child in srcNode.children) {
var threeObj = new THREE.Object3D();
// This line is the problem
threeObj.applyMatrix(threeMatrixFromSrcMatrix(child.matrix));
for(mesh in child.meshes) {
var threeMesh = threeMeshFromSrcMesh(mesh);
threeObj.add(threeMesh);
}
parentThreeObj.add(threeObj);
processNodes(child, threeObj); // And recurse!
}
}
Or at least that's what I'd like it to be. As I pointed out, the applyMatrix line doesn't work the way that I would expect. The majority of the scene looks okay, but certain elements that have been rotated aren't aligned properly (while other are, it's strange).
Looking through the COLLADA loader (which does approximately the same thing I'm trying to do) it appears that they decompose the matrix into a translate/rotate/scale and apply each individually. I tried that in place of the applyMatrix shown above:
var props = threeMatrixFromSrcMatrix(child.matrix).decompose();
threeObj.useQuaternion = true;
threeObj.position = props[ 0 ];
threeObj.quaternion = props[ 1 ];
threeObj.scale = props[ 2 ];
This, once again, yields a scene where most elements are in the right place but meshes that previously were misaligned have now been transformed into oblivion somewhere and no longer appear at all. So in the end this is no better than the applyMatrix from above.
Looking through several online discussions about the topic it seems that the recommended way to use matrices for your transforms is to apply them directly to the geometry, not the nodes, so I tried that by manually building the transform matrix like so:
function processNodes(srcNode, parentThreeObj, parentMatrix) {
for(child in srcNode.children) {
var threeObj = new THREE.Object3D();
var childMatrix = threeMatrixFromSrcMatrix(child.matrix);
var objMatrix = THREE.Matrix4();
objMatrix.multiply(parentMatrix, childMatrix);
for(mesh in child.meshes) {
var threeMesh = threeMeshFromSrcMesh(mesh);
threeMesh.geometry.applyMatrix(objMatrix);
threeObj.add(threeMesh);
}
parentThreeObj.add(threeObj);
processNodes(child, threeObj, objMatrix); // And recurse!
}
}
This actually yields the correct results! (minus some quirks with the normals, but I can figure that one out) That's great, but the problem is that we've now effectively flattened the scene hierarchy: Changing the transform on a parent will yield unexpected results on the children because the full transform stack is now "baked in" to the meshes. In this case that's an unacceptable loss of information about the scene.
So how might one go about telling Three.js to do the same logic, but at the appropriate point in the scene graph?
(Sorry, I would dearly love to post some live code examples but that's unfortunately not an option in this case.)
You can use matrixAutoUpdate = false to skip the Three.js scenegraph position/scale/rotation stuff. Then set object.matrix to the matrix you want and all should be dandy (well, it still gets multiplied by parent node matrices, so if you're using absolute modelview matrices you need to hack updateMatrixWorld method on Object3D.)
object.matrixAutoUpdate = false;
object.matrix = myMatrix;
Now, if you'd like to have a custom transformation matrix applied on top of the Three.js position/scale/rotation stuff, you need to edit Object3D#updateMatrix to be something like.
THREE.Object3D.prototype._updateMatrix = THREE.Object3D.prototype.updateMatrix;
THREE.Object3D.prototype.updateMatrix = function() {
this._updateMatrix();
if (this.customMatrix != null)
this.matrix.multiply(this.customMatrix);
};
See https://github.com/mrdoob/three.js/blob/master/src/core/Object3D.js#L209
Sigh...
Altered Qualia pointed out the solution on Twitter within minutes of me posting this.
It's a simple one-line fix: Just set matrixAutoUpdate to false on the Object3D instances and the first code sample works as intended.
threeObj.matrixAutoUpdate = false; // This fixes it
threeObj.applyMatrix(threeMatrixFromSrcMatrix(child.matrix));
It's always the silly little things that get you...
I make a graph with three.js and I would like to connect the nodes with THREE.Line.
After I move one of the nodes with mouse, must re create the edge (THREE.Line) with the nodes new coordinates. How can I make it?
my code does not update the view:
function render(){
newEdge.vertices.push(new THREE.Vertex(inNode.position));
newEdge.vertices.push(new THREE.Vertex(outNode.position));
var newLine = new THREE.Line(newEdge, new THREE.LineBasicMaterial({
color: 0xff0000,
opacity: 0.9
}));
scene.objects[edgePos] = newLine;
renderer.render(scene, camera);
}
Thanks a lot for any suggestion!
When you change the geometry directly like that, the render loop won't pick up on it. You need to flag the vertices as dirty with newEdge.__dirtyVertices = true;. You may also need newEdge.dynamic = true;. Look at section 9 here.
I've never used actionscript before, and but I've just had to dive into it in order to get a map working.
I'm using the following code to add a map marker, replacing a previous one if one exists:
public var tracer:Array = new Array();
public var tracerLng:Number = 0;
for ( var i : Number=1 ; i<64000 ; i++)
{
//Check if there is already a marker, if so get rid of it
if(tracerLng > 0) {
map.removeOverlay(tracer[0]);
tracer[0] = null;
tracer.pop();
}
// Set up a marker
var trackMrk:Marker = new Marker(
new LatLng(_lat, _lng),
new MarkerOptions({
strokeStyle: new StrokeStyle({color: 0x987654}),
fillStyle: new FillStyle({color: 0x223344, alpha: 0.8}),
radius: 12,
hasShadow: true
})
);
//Add the marker to the array and show it on the map
tracerLng = tracer.push(trackMrk);
map.addOverlay(tracer[0]);
}
My first problem is that running this code (The 64000 repeats are for testing, the final application won't need to be run quite THAT many times). Either way, memory usage increases by about 4kB/s - how do I avoid that happening?
Secondly - could anyone advise me on how to make that program more graceful?
Thanks in advance for advice
This isn't a memory leak, it's probably the result of created events - enter frames, mouse events, custom events etc. Provided that your memory doesn't keep going up and up forever, it's nothing to be worried about - it'll get garbage collected in due course.
Some points on your code:
The tracer Array doesn't seem to do anything - you only seem to be holding one thing in there at a time, so an array makes no sense. If you need an Array, use Vector instead. It's smaller and faster. More so if you create one with a specific length.
Don't create a new Marker unless you need one. Reuse old objects. Learn about object pooling: http://help.adobe.com/en_US/as3/mobile/WS948100b6829bd5a6-19cd3c2412513c24bce-8000.html or http://lostinactionscript.com/2008/10/30/object-pooling-in-as3/
The LatLng and MarkerOptions (including the stroke and fill objects) don't seem to change (I'm assuming the LatLng object lets you set a new position). If that's the case, don't create new ones when you don't need to. If you need to create new ones, StrokeStyle and FillStyle seem good candidates for a "create once, use everywhere" policy.
Create a destroy() function or similar in your Marker class and explicitly call it when you need to delete one (just before setting it to null or popping it from the array). In the destroy() function, null out any parameters to non-base classes (int, Number, String etc). Garbage collection runs using a reference counting method and a mark and sweep method. Ideally, you want to run everything using reference counting as it's collected quicker and stops any stalls in your program.
I explain memory management in AS3 a bit more here: http://divillysausages.com/blog/tracking_memory_leaks_in_as3
Also included is a class that helps you track down memory leaks if there are any