Fixing zoom 'jump' when scaling map - d3.js

I've been working with Jason Davies' Rotate the World and World Countries examples and incorporating a few other bits and pieces to learn about d3.
An example of what I have come up with so far is here.
The small selector in the top left corner will trigger a new 'trip' to be displayed on the globe and the centroid of the trip will be centered in view. This all works well, as does panning and zooming on the globe with the mouse.
However, there's an issue with the present implementation when one zooms in/out, alters the visible trip, then attempts to pan/zoom again: the zoom defaults back to the level it was before the trip change - resulting in a zoom 'jump' I'd like to remove [To see this behaviour, load my MNWE, zoom in with the mouse wheel perhaps 3 times, click the OK button to load 'J07', click on the globe holding the mouse button down and pan a bit - you'll see the jump I'm referring to.].
I'm pretty sure it's just me not updating the scale of my projection correctly, but I don't know enough to troubleshoot further. Jason has implemented a d3.geo.zoom function, which probably should be capable of fixing the issue. My attempt was to call it in my $("#sub").on("click" ... call via:
d3.select("#map").call(d3.geo.zoom().projection(proj).scale(a/2-10).on("zoom.redraw", function() {
d3.select(this).selectAll("path").attr("d", d3.geo.path().projection(proj));
}));
but that gave spurious results after the fact as well.
Lines 344 and 346:
var sc = d3.interpolate(proj.scale(), a / 2 - 10); //344
return function(i) {
proj.rotate(interp(i)).scale(sc(i)); //346
d3.select("#map").selectAll("path").attr("d", d3.geo.path().projection(proj));
//r.world();
};
are probably where I introduce the bug, as the rotation portion works without issue, it's only the scale additions that cause problems.

This can be solved by defining the zoom behavior in global scope.
m = d3.behavior.zoom();
Then update the zoom behavior with the scale in the transition as shown below this will stop the jump effect you have.
d3.transition().delay(250).duration(2250)
.tween("rotate", function() {
interp.source(proj.rotate()).target(coords).distance();
var sc = d3.interpolate(proj.scale(), a / 2 - 10);
return function(i) {
proj.rotate(interp(i)).scale(sc(i));
m.scale(sc(i));//update the zoom in the zoom behavior this will sop the jumping effect
d3.select("#map").selectAll("path").attr("d", d3.geo.path().projection(proj));
//r.world();
};
});
Working code here
Hope this helps.

Related

d3.zoom unexpected behavior: what am I missing?

I've got a behavior with d3.zoom whose solution I'm sure is to be found in something I'm obviously missing, but I can't seem to make sense of it. I've reviewed and reviewed examples, and seem to be following them precisely, but something is causing this particular function to not behave.
The following, rather than zoom to focusElement as intended, flips between zooming away from it, and then back to it. The values of -focusBBox['x'], for example, flip between the following two values on subsequent executions. 2500 is svgWidth/2
-208.586669921875
2500
function focusObject(focusElement) {
var focus = document.getElementById(focusElement);
var focusBBox = focus.getBoundingClientRect();
gridGroup.transition().duration(750).call(zoom.transform,d3.zoomIdentity.translate(-svgWidth / 2, -svgHeight / 2).translate(-focusBBox['x'], -focusBBox['y']));
}
Can someone just please take a moment to give a kind virtual slap to point out what it is that I'm missing?
Aha! From a previous incarnation of this particular endeavor, I was using a fixed-position SVG to capture mouse events and applying my transformations to a child SVG. The fact that getBbox() returns local coordinates and getBoundingClientRect() returns coordinates from the outer SVG coord system thus mucked things up.
I've included the revised snippet below. Note that focusBBox2 uses getBBox rather than getBoundingClientRect(), and that grid is the parent SVG as distinct from gridGroup in the original post.
function focusObject(focusElement) {
var focus = document.getElementById(focusElement);
var focusBBox2 = focus.getBBox();
grid.transition()
.duration(750)
.call(zoom.transform,d3.zoomIdentity.translate(focusBBox2.x, focusBBox2.y));
}

Three.js keep object static relative to outside container div - EDIT now with jsfiddle

So I have the basic setup of a three.js-canvas rendered inside of a html-div. Inside the 3d-world I want to position an object in such a way that it appears to be glued on to this outside div (should never move in any way). Currently I have this solution:
render {
object.position.x = camera.position.x;
object.position.y = camera.position.y;
object.position.z = camera.position.z - 200;
}
This works for panning the camera (i have rotation disabled since i don't need it). However, once I zoom in or out it obviously doesn't work any more, since zooming doesn't change the camera's position values. My approach was to incorporate the camera.zoom factor into the above function, but i couldn't get it to work properly. Is there an easy transformation function or something i can use?
Edit: I created a jsfiddle, hopefully this helps figuring out the solution. As long as you pan the camera with right mouse the yellow plane doesn't move at all (wanted behaviour). When you zoom in or out it starts to move (unwanted behaviour): https://jsfiddle.net/rdyLp7uc/2/

orthographic view on object with combined camera on three.js

i am trying to use the combined camera (found it under "Examples").
the problem is when i use it in Orthographic mode i still see the arrow and the box helper like in perspective view.
for example, when i am trying to zoom in with the mouse scroll, i can see the plane in the same size (as it supposed to be in orthographic view) but the arrows and the small box between the arrows is getting smaller or bigger.
when i tried to debug it at the renderer function i saw the camera is still in orthograpic mode when it render the arrows.
any idea how can i make all the object to be in orthograpic view but still use the combined camera?
edit:
i am not sure which part of the code i should post so i add a picture to describe my problem.
you can see that i am in an orthographic camera and i'm trying to zoom in and i can see the axis arrow getting bigger or smaller.
the difference between the plane when zooming
Found a possible answer which worked for me:
under TransformControls.js
change the update function to:
scale = (camera.inOrthographicMode == true)? 100 : worldPosition.distanceTo(camPosition ) / 6 * scope.size;

Orbit Controls zoom issue

I am trying to get infinite zoom functionality,
While looking at the following three.js example:
http://threejs.org/examples/misc_controls_orbit.html
While getting closer to the target vector, the zoom factor reduced and reduced until it barely worked, for an end user it looks like it is stuck. panning won't really work, and in order to zoom out user needs to use the mouse wheel for like ages.
Is there a way to get infinite zoom functionality?
Thanks.
as mentioned it is zooming infinitely ( but it's zooming infinitely to the target at the center of the scene so it will never pass the target and thus never zoom beyond the center of the scene ), there's a couple changes you can make to the controls object in order to render the desired effect ( if I'm understanding you correctly ).
one small change you can make is change the target location from the center of the scene to the other end of the scene, given the the camera's far perameter is set to 1000 we can try setting the target's z position to -1000 in your init() function like so:
controls.target = new THREE.Vector3(0,0,-1000);
here's a working fiddle: http://jsfiddle.net/6gn7k0m4/
in that example you'll zoom past the scene and slow down when you get to -1000. Another approach might be to change the dollyIn and dollyOut methods so that rather than getting infinitely closer to the target what it does is change the camera's z index, this way when you zoom past the target the camera will still rotate around the center, you can do something like this
controls.dollyOut = function(){
this.object.position.z -= 100;
}
controls.dollyIn = function(){
this.object.position.z += 100;
}
here's a working fiddle for that example: http://jsfiddle.net/6gn7k0m4/1/

d3.event.y has strange values during drag behavior

I'm creating an example illustrating a layout with resizeable cells using the D3 drag behaviour and CSS {display: table} styles. It works fine for dragging horizontally, but not vertically. For vertical resizing, d3.event.y is providing values that do not make sense to me.
Here is a fiddle showing the working horizontal drag and the broken vertical drag. Take a look at the console output while dragging to see that the values returned by d3.event match the values returned by d3.mouse() for the horizontal drag, but they diverge for the vertical drag.
I can fix the behaviour by using the d3.mouse() y-coordinate instead of the d3.event y-coordinate. To see this, comment out the "DOESN'T WORK" line and uncomment the "WORKS" line. However, I don't understand why I need to do this, and it seems less general in that I have to assume a mouse input instead of using the more generic d3.event.
Is this a bug, or am I failing to understand something here?
Note that this question seems to be hitting the same issue, but using HTML tables instead of CSS tables. I thought it would be helpful to document that this problem is occurring in both contexts.
Also note that commenting out the two lines that actually do the vertical resizing, commented with "RESIZE CELLS", makes the d3.event work correctly. Of course, the table doesn't get resized then. This suggests that it is something about the act of resizing the divs that is leading d3.event astray.
Alright, I think I've figured out the issue. If you look at the code for drag behavior, you'll notice in dragstart that the value used to calculate the mouse offset is based off this.parentNode. In short, it uses this.parentNode as a reference point, and assumes that it's going to be stable for the duration of the drag. You're modifying the parent nodes during the drag, so its reference point gets, to put it technically, pretty borked. In this case, using d3.mouse is your best bet, since d3.event.y is only going to be reliable as long as the parent node stays in place.
The reason this only happens in the y direction for you is that the x position of all the rows, which are the parent nodes here, stay constant, whereas the y component changes during the drag.
The relevant code sections:
parent = that.parentNode,
function moved() {
var position1 = position(parent, dragId), dx, dy;
//...
dispatch({
type: "drag",
x: position1[0] + dragOffset[0],
y: position1[1] + dragOffset[1],
dx: dx,
dy: dy
});

Resources