I am developing an app where I get a user location from the server and I display it on the map.
I am also provided with an angle so I can display in which direction the user is looking.
There is also a possibility to open a sidepanel for some more information. In this situation I have to move the map center left which I do like this.
var center = map.getView().getCenter();
var pan = ol.animation.pan({
duration: 500,
source: (center)
});
if (isSidePanelVisible) {
center[0] += 1000;
} else {
center[0] -= 1000;
}
map.beforeRender(pan);
map.getView().setCenter(center);
This works great while the user is "looking" north but not when I rotate the map by an angle. Is there a quick way to pan the map always left by a certain amount regardles of angle?
You could use ol.Map.html#getPixelFromCoordinate to convert the center to pixel coordinates, do your calculation and then convert back to real coordinates with ol.Map.html#getCoordinateFromPixel:
var view = map.getView();
var center = view.getCenter();
var centerInPx = map.getPixelFromCoordinate(center);
var newCenterInPx = [centerInPx[0] - 100, centerInPx[1]];
var newCenter = map.getCoordinateFromPixel(newCenterInPx);
view.setCenter(newCenter);
http://jsfiddle.net/6mh110xv/1/
To pan left , right , bottom , top , you can use the following code :
panMap(direction: string) {
let newCenterInPx;
let center = map.getView().getCenter();
let centerInPx = map.getPixelFromCoordinate(center);
switch (direction) {
case 'left': newCenterInPx = [centerInPx[0] - 100, centerInPx[1]]; break;
case 'right': newCenterInPx = [centerInPx[0] + 100, centerInPx[1]]; break;
case 'top': newCenterInPx = [centerInPx[0], centerInPx[1] - 100]; break;
case 'bottom': newCenterInPx = [centerInPx[0], centerInPx[1] + 100]; break;
}
var newCenter = map.getCoordinateFromPixel(newCenterInPx);
map.getView().setCenter(newCenter);
}
Related
I need to orient one node to point its Z-axis at another node in 3D. Yeah, the perfect job for the LookAtConstraint. And for most of my work LookAt is fine. But when I apply LookAt to a particular node, I can no longer animate that node's translation with SCNAction. Picture a hydrogen atom leaving a molecule as it ionizes. The orientation is needed to properly rotate the bond (a cylinder) bewteen the hydrogen and an oxygen atom on the molecule.
I can orient the bond FROM the oxygen TO the hydrogen and animate. But this disorients most of the other bonds which were getting by just fine with LookAt's.
I gave this a mighty try before realizing it answers a somewhat different question:
Calculate rotations to look at a 3D point?
I had a similar issue with a project. What I eventually realized was that I need to use multiple constraints. One for translation (movement) and the other using the look at constraint.
I would move the object and then apply the look at constraint; in this case, it was a camera following an objects being moved using actions. Code snippet follows:
let targetNodeConstraint = SCNLookAtConstraint(target: someObject)
targetNodeConstraint.gimbalLockEnabled = true
let followObjectConstraint = SCNTransformConstraint(inWorldSpace: true, withBlock: { (node, matrix) -> SCNMatrix4 in
let transformMatrix = SCNMatrix4MakeTranslation(
self.someObject.position.x - 1.0,
self.someObject.position.y, self.someObject.position.z + 1.0)
return transformMatrix
})
// Position the object behind the other object & rotate it to
roadCamera.constraints = [followObjectConstraint, targetNodeConstraint]
The important thing to note is the order in which the constraints are added to the object using an array. In the code above, I am ignoring the current matrix before I apply a transform matrix (I should re-write this code someday)
The complete source code of this "experiment" is on GitHub as I try things out.
https://github.com/ManjitBedi/CubeTrip
Hopefully, this is helpful.
My solution here. Deal with situation that node continuously translate in space and should always toward a position.
#discardableResult
func yew(_ node:SCNNode, toPosition position:SCNVector3) -> Float
{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
forward = GLKVector3Normalize(GLKVector3Make(forward.x, 0, forward.z))
toWard = GLKVector3Normalize(GLKVector3Make(toWard.x, 0, toWard.z))
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var yew = acos(dotProduct)
if yew < 0 {
assert(false)
}
//toward is clockwise of forward
let isCW = GLKVector3CrossProduct(forward, toWard).y < 0
if isCW {
yew = -yew
}
eularAngle.y = yew
node.eulerAngles = SCNVector3Make(eularAngle.x + wrapperNode.eulerAngles.x,
eularAngle.y + wrapperNode.eulerAngles.y,
eularAngle.z + wrapperNode.eulerAngles.z)
return yew
}
#discardableResult
func pitch(_ node:SCNNode, toPosition position:SCNVector3) -> Float{
var eularAngle = SCNVector3Zero
let tranform = node.transform
var toWard = GLKVector3Make(position.x - node.position.x, position.y - node.position.y, position.z - node.position.z)
var forward = GLKVector3Make(tranform.m31, tranform.m32, tranform.m33)
forward = GLKVector3Normalize(forward)
toWard = GLKVector3Normalize(toWard)
var dotProduct = GLKVector3DotProduct(forward,toWard)
dotProduct = (dotProduct > 1) ? 1 : ((dotProduct < -1) ? -1 : dotProduct)
var pitch = acos(dotProduct)
//toward is clockwise of forward, if right vector of model and crossProfuct.x has same direction
let crossProduct = GLKVector3CrossProduct(forward, toWard)
let isCW = (crossProduct.x <= 0) != (tranform.m11 <= 0)
if isCW {
pitch = -pitch
}
eularAngle.x = pitch
node.eulerAngles = SCNVector3Make(eularAngle.x + node.eulerAngles.x,
eularAngle.y + node.eulerAngles.y,
eularAngle.z + node.eulerAngles.z)
return pitch
}
func orient(_ node:SCNNode, toPosition position:SCNVector3) {
self.yew(node, toPosition: position)
self.pitch(node, toPosition: position)
}
I understand that I can use body.position.set(x, y, z) to instantaneously move a body, but how can I move it smoothly in an animated manner where it's movement will adhere to the physics and collide with any other bodies on its journey? Using body.velocity.set(x, y, z) will set its velocity, and using body.linearDamping = v, will provide some friction/resistance... but it's still not good enough to allow me to specify exactly where I want the body to stop.
It sounds like you're looking for a kinematic body. With kinematic bodies, you have full control over the movement, and it will push away other bodies in its path. However, the body has infinite mass and is not affected by other bodies colliding with it.
Start off by defining the start and end positions of your body.
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
Then create your kinematic body. In this example we'll add a Box shape to it.
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
Compute the direction vector and get total length of the tween path.
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
The speed and velocity can be calculated using the formula v = s / t.
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
For each update, compute the tween progress: a number between 0 and 1 where 0 i start position and 1 is end position. Using this number you can calculate the current body position.
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
// Calculate current position
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
// We passed the end position! Stop.
body.velocity.set(0,0,0);
body.position.copy(endPosition);
}
See full code below. You can duplicate one of the cannon.js demos and just paste this code.
var demo = new CANNON.Demo();
var postStepHandler;
demo.addScene("Tween box",function(){
var world = demo.getWorld();
// Inputs
var startPosition = new CANNON.Vec3(5, 0, 2);
var endPosition = new CANNON.Vec3(-5, 0, 2);
var tweenTime = 3; // seconds
var body = new CANNON.Body({
mass: 0,
type: CANNON.Body.KINEMATIC,
position: startPosition
});
body.addShape(new CANNON.Box(new CANNON.Vec3(1,1,1)));
world.add(body);
demo.addVisual(body);
if(postStepHandler){
world.removeEventListener('postStep', postStepHandler);
}
// Compute direction vector and get total length of the path
var direction = new CANNON.Vec3();
endPosition.vsub(startPosition, direction);
var totalLength = direction.length();
direction.normalize();
var speed = totalLength / tweenTime;
direction.scale(speed, body.velocity);
// Save the start time
var startTime = world.time;
var offset = new CANNON.Vec3();
postStepHandler = function(){
// Progress is a number where 0 is at start position and 1 is at end position
var progress = (world.time - startTime) / tweenTime;
if(progress < 1){
direction.scale(progress * totalLength, offset);
startPosition.vadd(offset, body.position);
} else {
body.velocity.set(0,0,0);
body.position.copy(endPosition);
world.removeEventListener('postStep', postStepHandler);
postStepHandler = null;
}
}
world.addEventListener('postStep', postStepHandler);
});
demo.start();
You need to use a physics library for this, such as Physijs. It works easily with Three.js. Googling for "Physijs Three.js" will provide examples.
I have to set a limit for the movement of a circle when the coordinate 'y' is on the pixel 'y-15', on a specific coordinate (x,y). I've tried to use 'draggable=true' with the event 'mouseup' and 'mousedown', but I can't get the results that I need. Can someone help me, please?
My code is:
circle.on('mouseover',function(){
this.setAttr('draggable',true);
});
circle.on('dragmove',function(e){
var mousePos = stage.getMousePosition();
var x = mousePos.x;
var y = mousePos.y;
if(y < y2- 15){
//do anything
}
else{
this.setAttr('draggable',false);
}
});
If I understand correctly, what you want is dragBoundFunc.
See this tutorial:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-bounds-tutorial-with-kineticjs/
// bound below y=50
var blueGroup = new Kinetic.Group({
x: 100,
y: 70,
draggable: true,
dragBoundFunc: function(pos) {
var newY = pos.y < 50 ? 50 : pos.y;
return {
x: pos.x,
y: newY
};
}
});
You'll just have to modify your code to look for y-15 when you grab your mouse coordinates, and set the dragBoundFunc to reflect the coordinates you clicked on.
If you're trying to limit the y-axis on a known fixed y value, then this becomes even easier, exactly like how the tutorial shows you, except you just have to change the coordinate that you want to limit Y to.
Check this one as a quick reference too:
http://www.html5canvastutorials.com/kineticjs/html5-canvas-drag-and-drop-shapes-horizontally-or-vertically-tutorial/
I am using the code snippet from this stackoverflow question to label my flot data points. So far this has served me well, but now I have to label the overall values of stacked bars. There are two data series and so far I've managed to calculate the sums, but I can't seem to work out a proper positioning for my labels. I'd like to place them on top of the stacks, but pointOffset only gives me the offsets based on non-stacked bars.
This is the code I am currently using, it places the labels where the second series' data points would be, if the bars weren't stacked, which puts them somewhere in the top bars.
$.each(p.getData()[1].data, function(i, el){
var series0 = p.getData()[0].data;
sum = el[1] + series0[i][2]
var o = p.pointOffset({x: el[0], y: el[1]});
$('<div class="data-point-label">' + sum + '</div>').css( {
position: 'absolute',
left: o.left - 5,
top: o.top ,
display: 'none'
}).appendTo(p.getPlaceholder()).fadeIn('slow');
});
Edit #1: So far I've tried using c2p/p2c, calculating the top value using the single data points' top values and finding more documentation on the stack plugin. I am afraid none of this has helped me much.
Edit #2: I've also tried the code given in this stackoverflow answer but it doesn't work for me. I suspect the author is using some label plugin ...
The solution to put the labels in the top of the bars in stack usinjg canvas is that you have to calculate the xPoint in base of the sum of the values in the complete stack.
Here is a example of code
var sumaArr = [];
for (var u = 0; u < p.getData().length; u++) {
$.each(p.getData()[u].data, function (i, el) {
sumaArr[i] > 0 ? sumaArr[i] = sumaArr[i] + el[1] : sumaArr[i] = el[1];
});
}
var ctx = p.getCanvas().getContext("2d");
var data = p.getData()[p.getData().length - 1].data;
var xaxis = p.getXAxes()[0];
var yaxis = p.getYAxes()[0];
var offset = p.getPlotOffset();
ctx.font = "12px 'Segoe UI'";
ctx.fillStyle = "gray";
for (var i = 0; i < data.length; i++) {
var text = sumaArr[i];
var metrics = ctx.measureText(text);
var xPos = (xaxis.p2c(data[i][0]) + offset.left) - metrics.width / 2;
var yPos = yaxis.p2c(sumaArr[i]) + offset.top - 5;
ctx.fillText(text, xPos, yPos);
}
The var yPos use the sume of the values that make the complete stack.
I have just started using jqPlot for a line chart with multiple series. It seems great.
I have added the Cursor plugin with the intention of showing a vertical line on the nearest data point on the x axis. In other words, it snaps to the nearest point. The Cursor plugin, however always shows the vertical cursor right where the mouse is.
It seems like I just want to "override" or replace moveLine to change the current functionality.
What's the most appropriate way of doing so?
It seems a little much to copy/past all of the cursor plugin just to modify a very small subset.
Thanks!
I know I'm a kind of archaeologist by edited this post but I think the following can be useful for someone (I hope).
I've made a piece of code which allow to draw a vertical line following the cursor and displaying a tooltip on the nearest point (according to the cursor). You can find a demo of it on this JSFiddle.
I also post the corresponding code below (only the part which calculate nearest point and display tooltip):
//Show nearest point's tooltip
$("#chart1").bind('jqplotMouseMove', function(ev, gridpos, datapos, neighbor, data){
var c_x = datapos.xaxis;
var index_x = -1;
var pos_index = 0;
var low = 0;
var high = data.data[0].length-1;
while(high - low > 1){
var mid = Math.round((low+high)/2);
var current = data.data[0][mid][0];
if(current <= c_x)
low = mid;
else
high = mid;
}
if(data.data[0][low][0] == c_x){
high = low;
index_x = high;
}else{
var c_low = data.data[0][low][0];
var c_high = data.data[0][high][0];
if(Math.abs(c_low - c_x) < Math.abs(c_high - c_x)){
index_x = low;
}else{
index_x = high;
}
}
//Display marker and tooltip
if(data.series[0].data[index_x]){
var x = data.series[0].gridData[index_x][0];
var y = data.series[0].gridData[index_x][1];
var r = 5;
var highlightCanvas = $(".jqplot-highlight-canvas")[0];
var context = highlightCanvas.getContext('2d');
context.clearRect(0,0,highlightCanvas.width,highlightCanvas.height);
context.strokeStyle = 'rgba(47,164,255,1)';
context.fillStyle = 'rgba(47,164,255,1)';
context.beginPath();
context.arc(x,y,r,0,Math.PI*2,true);
context.closePath();
context.stroke();
context.fill();
//Display tooltip on nearest point
var highlightTooltip = $(".jqplot-highlighter-tooltip");
var val_x = data.data[0][index_x][0];
var val_y = data.data[0][index_x][1];
highlightTooltip.html("X : "+val_x+"<br/>Y : "+val_y);
highlightTooltip.css({'left': x+'px', 'top': (y-10)+'px', 'display': 'block'});
}
});
Feel please to use it and to modify it as you need it.
Try a bar graph series on top of everything else that has alpha set to 0.