I am new to d3. When do pan and zoom with d3v4, I noticed we always have to create new scale based on the d3.event.transform and apply the new scale to everything rather than replace the old scale with new and apply the old scale again. For example, this zoom function works perfect:
function zoomFunction(){
// create new scale ojects based on event
var new_xScale = d3.event.transform.rescaleX(xAxisScale)
var new_yScale = d3.event.transform.rescaleY(yAxisScale)
console.log(d3.event.transform)
// update axes
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
// update circle
circles.attr("transform", d3.event.transform)
};
This zoom function screws everything:
function zoomFunction(){
// create new scale ojects based on event
var new_xScale = d3.event.transform.rescaleX(xAxisScale)
var new_yScale = d3.event.transform.rescaleY(yAxisScale)
xAxisScale = new_xScale;
yAxisScale = new_yScale;
console.log(d3.event.transform)
// update axes
gX.call(xAxis.scale(xAxisScale));
gY.call(yAxis.scale(yAxisScale));
// update circle
circles.attr("transform", d3.event.transform)
};
The full example is here: https://bl.ocks.org/rutgerhofste/5bd5b06f7817f0ff3ba1daa64dee629d
The reason I want to use the new scale to replace the old one is that in my case a lot of parts of svg are related to scale. If I simply change the old scale, those binded events will work perfectly. I don't want to reclaim everything in my zoomFunction.
So you wish to change
xAxisScale = new_xScale;
yAxisScale = new_yScale;
inside the zoom function for some reason.
If you do it the rescale will not work as expected.
var new_xScale = d3.event.transform.rescaleX(xAxisScale)
var new_yScale = d3.event.transform.rescaleY(yAxisScale)
Reason: xAxisScale andyAxisScale should be the original, because the rescale is calculated based/relative to the original scale value.
One way to get around this would be to make a copy of the original
x and y scale.
// create scale objects
var xAxisScale =d3.scaleLinear()
.domain([-200,-100])
.range([0,width]);
var yAxisScale = d3.scaleLinear()
.domain([-10,-20])
.range([height,0]);
//make a copy of the scales
var copyX = xAxisScale.copy();
var copyY = yAxisScale.copy();
// create axis objects
Next in your zoomFunction do :
function zoomFunction(){
// create new scale ojects based on event
var new_xScale = d3.event.transform.rescaleX(copyX)
var new_yScale = d3.event.transform.rescaleY(copyY)
//changing scale due to some reason.
xAxisScale = new_xScale;
yAxisScale = new_yScale;
// update axes
gX.call(xAxis.scale(xAxisScale));
gY.call(yAxis.scale(yAxisScale));
// update circle
circles.attr("transform", d3.event.transform)
};
working code here
Related
I have been working with leaflet-editable.js to allow the editing of shapes. Specifically I am creating a rectangle that will maintain an aspect ratio of 4:3 when a corner is dragged. I have created a function to calculate the aspect ratio and return the lat/lng of where the new corners should be drawn.
I have attached this function to the event "editable:vertex:drag".
I'm not sure how to update the actual drawing of the rectangle to keep the scale. I have tried setting the object properties to the new bounds which updates but doesn't change the rectangle.
I think the answer is in refreshing the drawing of the rectangle but I don't know how to get the current rectangle instance nor how to refresh it.
Javascript is new for me
carto.on('editable:vertex:drag', function(e) {
console.log(e);
var cornersNe = e.vertex.latlngs[0], //ne
cornersNw = e.vertex.latlngs[1], //nw
cornersSw = e.vertex.latlngs[2], //sw
cornersSe = e.vertex.latlngs[3]; //se
var distanceWidth = carto.distance(cornersNw, cornersNe).toFixed(2);
var distanceHeight = carto.distance(cornersSw, cornersNw).toFixed(2);
var asR = aspectRatio(distanceWidth, distanceHeight, e.latlng.lat, e.latlng.lng);
var index = e.vertex.getIndex(),
next = e.vertex.getNext(),
previous = e.vertex.getPrevious(),
oppositeIndex = (index + 2) % 4,
opposite = e.vertex.latlngs[oppositeIndex],
bounds = new L.LatLngBounds(asR, opposite);
// L.marker(bounds).addTo(carto);
console.log('first set of bounds ', e.layer._bounds);
console.log('bounds to set ', bounds);
e.layer._bounds = bounds;
console.log('Updated set ',e.layer._bounds);
I am open to other ways of doing it if this is too roundabout.
Use the public function to set the bounds:
e.layer.setBounds(bounds);
I'm having a three.js object in my scene, which you can rotate/scale/move with the THREE.TransformControls. After positioning the object in the scene, the values of rotation/position/scale should be saved. I'm getting the values like this :
// Position
$scope.scene.updateMatrixWorld(true);
var position = new THREE.Vector3();
position.getPositionFromMatrix( $scope.object.matrixWorld );
// Rotation
var rotation = new THREE.Euler();
$scope.object.getWorldRotation(rotation);
// Size
var size = new THREE.Box3().setFromObject($scope.object);
var x = (size.max.x - size.min.x);
var y = (size.max.y - size.min.y);
var z = (size.max.z - size.min.z);
So, when I'm saving the values and reload the scene with the new values, the object looks really different than it should. So, I think, the functions deliver the wrong values for rotation/scale/position. For example, the size.z variable should always be 0, but when saving (and printing the value in the console) it gets an other value.
Does anyone have an idea why ?
Thanks for helping.
Three.js offers a matrix decomposition function: https://threejs.org/docs/#api/math/Matrix4.decompose
Here's an example of how to use it:
var translation = new THREE.Vector3();
var rotationQ = new THREE.Quaternion();
var scale = new THREE.Vector3();
myObject.matrixWorld.decompose(translation, rotationQ, scale);
Then if you want to save the rotation as an Euler:
var rotationE = new THREE.Euler().setFromQuaternion(rotationQ.normalize());
three.js r88
I need to fill a rectangle with an image. I've tried Raster but I am not able to figure out how to use raster inside the rectangle created on the canvas.
Is there a function similar to fillColor() method for filling the rectangle with an image rather than colors?
Any hints/tips or a sample fiddle would be great!
Here is what you can do:
// Create your raster:
var url = 'http://assets.paperjs.org/images/marilyn.jpg';
var raster = new Raster(url);
raster.position = new Point(300,300);
// Use clipMask to create a custom polygon clip mask:
var path = new Path.Rectangle(150,150,100,150);
path.clipMask = true;
// It is better to add the path and the raster in a group (but not mandatory)
/*
var group = new Group();
group.addChild(raster);
group.addChild(path);
*/
// If you just need a rectangle part of a raster, you could use getSubRaster(rect) instead:
/*
var subRaster = raster.getSubRaster(new Rectangle(150,150,100,150));
subRaster.position = new Point(600,600);
*/
I am fairly new to cocos 2d and chipmunk. So far i have managed to create a static object and add collision handler to it. But i want to create a dynamic.
this.space = space;
this.sprite = new cc.PhysicsSprite("#rock.png");
var body = new cp.StaticBody();
body.setPos(pos);
this.sprite.setBody(body);
this.shape = new cp.BoxShape(body,
this.sprite.getContentSize().width,
this.sprite.getContentSize().height);
this.shape.setCollisionType(SpriteTag.rock);
this.space.addStaticShape(this.shape);
spriteSheet.addChild(this.sprite);
But i want to create a dynamic body similarly which moves for example by:
cp.v(310, 0), cp.v(0, 0)
This is very basic I know but I would grateful if someone would help me with this. Also if you have good documentation in of cocos 2d and chipmunk in JS. do share it. Thanks
Here you go:
//Add the Chipmunk Physics space
var space = new cp.Space();
space.gravity = cp.v(0, -10);
//Optionally add the debug layer that shows the shapes in the space moving:
/*var debugNode = new cc.PhysicsDebugNode(space);
debugNode.visible = true;
this.addChild(debugNode);*/
//add a floor:
var floor = new cp.SegmentShape(this.space.staticBody, cp.v(-1000, 10), cp.v(1000, 0), 10);
//floor.setElasticity(1);
//floor.setFriction(0);
space.addStaticShape(floor);
//add a square to bounce
//the Sprite
var mySprite = cc.PhysicsSprite.create("res/something.png");
gameLayer.addChild(mySprite);
//the Body
var size = mySprite.getContentSize();
var innertialMomentum = 1; //Use Infinity if you want to avoid the body from rotating
var myBody = new cp.Body(innertialMomentum , cp.momentForBox(innertialMomentum , size.width, size.height));
mySprite.setBody(myBody);
space.addBody(myBody);
//myBody.p = cc.p(xxx, yyy); //To alter the position of the sprite you have to manipulate the body directly, otherwise it won't have the desired effect. You can also access it by mySprite.body
//the Shape
var myShape = new cp.BoxShape(myBody, size.width, size.height);
//myShape.setElasticity(1);
//myShape.setFriction(0);
space.addShape(myShape);
//Apply your desired impulse
//mySprite.body.applyImpulse(cp.v(ix,iy), cp.v(rx,ry)); // Where the first vector is for the impulse strength and direction and the second is for the offset between the center of the object and where you want the impulse to be applied.
I get the current bounds of a google map by calling:
map.getBounds()
This will return LatLngBounds. For example:
((-26.574013562724005, -1.5375947819336488), (-25.230016040794602, 3.735842718066351))
Is it possible to get the the new LatLng points if I want to increase the map bounds by a percentage (e.g. 1%) around the centre of the map.
I have tried multiplying every coordinate by 1.01 but this shifts the map and does not increase the bounds.
I want to use the increased box size to start caching markers, in order to save calls to the server when the user pans or zooms out.
The image below will explain better what needs to happen:
Based on markerclusterer getExtendedBounds function you could use this one:
function getExtendedBounds(map, overlay) {
//With _mapBoundsEnlargePixels we add some extra space surrounding the map
//change this value until you get the desired size
var _mapBoundsEnlargePixels = 500;
var projection = overlay.getProjection();
var bounds = angular.copy(map.getBounds());
// Turn the bounds into latlng.
var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
bounds.getNorthEast().lng());
var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
bounds.getSouthWest().lng());
// Convert the points to pixels and extend out
var trPix = projection.fromLatLngToDivPixel(tr);
trPix.x = trPix.x + _mapBoundsEnlargePixels;
trPix.y = trPix.y - _mapBoundsEnlargePixels;
var blPix = projection.fromLatLngToDivPixel(bl);
blPix.x = blPix.x - _mapBoundsEnlargePixels;
blPix.y = blPix.y + _mapBoundsEnlargePixels;
// Convert the pixel points back to LatLng
var ne = projection.fromDivPixelToLatLng(trPix);
var sw = projection.fromDivPixelToLatLng(blPix);
// Extend the bounds to contain the new bounds.
bounds.extend(ne);
bounds.extend(sw);
return bounds;
}