Animating Paper.js path segments & handle info - javascript

I'm trying to animate between two complex paths using Paper.js and Tween.js. I've gotten pretty close, I can move all of the points in the path to the correct final positions, but I'm having problems with the handleIn and handleOut for each segment. It doesn't seem to be updating them.
Here's my code: http://codepen.io/anon/pen/rVBaZV?editors=101
var endPathData = 'M740,342.9c-32,...etc...';
var endPath = new Path(endPathData);
endPath.fillColor = '#4CC7A4';
beginPathData = 'M762.8,262.8c-48,...etc...';
var beginPath = new Path(beginPathData);
beginPath.fillColor = '#FFC1D1';
var numberOfSegments = beginPath.segments.length;
for (var i = 0; i < numberOfSegments; i++) {
var tween = new TWEEN.Tween(beginPath.segments[i].point)
.to({
x: endPath.segments[i].point.x,
y: endPath.segments[i].point.y
}, 3000)
.easing(TWEEN.Easing.Linear.None)
.start();
}
view.draw();
view.onFrame = function (event) {
TWEEN.update();
};
I'd like the pink path to end up exactly like the green one, but now I'm stuck. Is there anyway to achieve this?

You need to tween the handles too.
Each segment has two handles: segment.handleIn and segment.handleOut
in your example code you tween the segment.point (the segments position) resulting in the right location of the segments.
I don't know your Tween library, so it is up to you to implement it.
But It looks like you can add to more new tween one for the
beginPath.segments[i].handleIn
and one for the
beginPath.segments[i].handleOut
You can easily check that your code is right by letting paperjs smooth your path and taking care of the handles. By updating the onFrame function like this:
view.onFrame = function (event) {
TWEEN.update();
beginPath.smooth();
endPath.smooth();
};
and this results in the same shaped path.

Related

Leaflet - How to delete arcs from map?

So I have a map on which I draw trade routes(as arcs) between countries with arc.js. The arcs are different based on the year, and I basically want to be able to redraw the arcs based on what year I pick.
I create the arcs like so:
//for every trade route, draw a arc from start to end
var line_to_add;
for(var q = 0; q < dest_Coor.length; q++) {
//create the arc
var start = {x: from_Coor[0][0], y: from_Coor[0][1]};
var end = {x: dest_Coor[q][0], y: dest_Coor[q][1]};
var generator = new arc.GreatCircle(start,end);
var line = generator.Arc(100, {offset: 10});
//add it to map
line_to_add = L.geoJson(line.json(), {
color: "#004c00"
});
//add the arc to global array of arcs
allArrows.push(line_to_add);
line_to_add.addTo(map);
}
When a different year is picked, I then try to delete the current arcs through this method, with allArrows being a global array of all arc objects(according to this question):
function deleteOldArcs() {
for(var i = 0; i < allArrows.length; i++) {
map.removeLayer(allArrows[u]);
}
//then reset the list of arrows:
allArrows.length = 0;
}
However, this doesn't work and program execution just freezes inside map.removeLayer(allArrows[u]), and I don't understand why. I checked the list of map layers with map._layers, and the arc objects are in there... so why would calling map.removelayer not work?
Any help would be greatly appreciated, thanks!!
You've got several options:
Use Leaflet.Arc. It will create Leaflet layers directly, which might be easier to manage than the json blobs.
Store line_to_add somewhere in an array, and call its remove() method later. From what I see, you want to reference allArrows[u], but you never fill up the allArrows data structure.
Use a L.LayerGroup. Add the LayerGroup to the map, the arcs to the LayerGroup, then call its clearLayers() method.

Cesium - using camera to scale a polygon to match Lat-Lon positions while zoom-in/zoom-out

I am struggling with camera functionality that (I think) would provide a way to force my polygon to stick to the top of my house on zoom-out, zoom-in, and rotation (or camera move).
This question follows an earlier question that was resolved. Now I need a little help resolving my next issue.
The sample code I am trying to follow is located in the gold standard that appears to be baked into the existing camera controller here.
pickGlobe is executed with the parameters of the viewer, the correct mousePosition in world coordinates and a result parameter, which I don't care about right now. scene.pickPosition takes the c2position (Cartesian2) and should return the scratchDepthIntersection (Cartesian3). Instead, the returned value is undefined.
Here is my code:
function clickAction(click) {
var cartesian = scene.camera.pickEllipsoid(click.position, ellipsoid);
if (cartesian) {
var setCartographic = ellipsoid.cartesianToCartographic(cartesian);
collection.latlonalt.push(
Cesium.Math.toDegrees(setCartographic.latitude).toFixed(15),
Cesium.Math.toDegrees(setCartographic.longitude).toFixed(15),
Cesium.Math.toDegrees(setCartographic.height).toFixed(15)
);
lla.push(Cesium.Math.toDegrees(setCartographic.longitude), Cesium.Math.toDegrees(setCartographic.latitude));
if (lla.length >= 4) {
console.log((lla.length / 2) + ' Points Added');
}
enableDoubleClick();
enableDraw();
testMe(click.position); <--------------------- straight from the mouse click
}
}
var pickedPosition;
var scratchZoomPickRay = new Cesium.Ray();
var scratchPickCartesian = new Cesium.Cartesian3();
function testMe(c2MousePosition) { <--------------------- straight from the mouse click
if (Cesium.defined(scene.globe)) {
if(scene.mode !== Cesium.SceneMode.SCENE2D) {
pickedPosition = pickGlobe(viewer, c2MousePosition, scratchPickCartesian);
} else {
pickedPosition = camera.getPickRay(c2MousePosition, scratchZoomPickRay).origin;
}
}
}
var pickGlobeScratchRay = new Cesium.Ray();
var scratchRayIntersection = new Cesium.Cartesian3();
var c2position = new Cesium.Cartesian2();
function pickGlobe(viewer, c2MousePosition, result) { <--------------------- straight from the mouse click
c2position = c2MousePosition; <--------------------- setting to Cartesian2
var scratchDepthIntersection = new Cesium.Cartesian3();
if (scene.pickPositionSupported) {
scratchDepthIntersection = scene.pickPosition(c2MousePosition); <--------------------- neither works!
}
}
Here are my variables:
Here is the result:
Here are my questions to get this code working:
1. Why is scratchDepthIntersection not getting set? c2position is a Cartesian2 and c2MousePosition is straight from the mouse.click.position and scratchDepthIntersection is a new Cartesian3.
The correct value for mousePosition is a Cartesian2 containing window coordinates, not a Cartesian3. Such mouse coordinates usually come from a callback from Cesium.ScreenSpaceEventHandler, but can also be constructed from native JavaScript mouse/touch events.
If you inspect the contents of mousePosition, you should find x and y values in window pixel coordinates.
I see you edited the question to include the contents of mousePosition, and it looks like the mouse coordinates have already been converted into ellipsoid Cartesian3 coordinates, which will prevent this code from working. You want original mouse coordinates going directly into scene.pickPosition for this to work.

event data in pixi.js

I just started playing around with pixi and have drawn multiple rectangles from an array with pixel coordinates like this:
var rectangle = [....];
....
var stage = new PIXI.Stage();
var renderer = PIXI.autoDetectRenderer(wrapper.getWidth(), wrapper.getHeight(), { transparent: true });
....
var graphics = new PIXI.Graphics();
graphics.interactive = true;
graphics.on("mouseover", function(e) {
this.alpha = 0.5;
}).on("mouseout", function() {
this.alpha = 1;
});
graphics.beginFill(0xFFFFFF);
graphics.lineStyle(2, 0x000000);
for (var i = 0; i < rectangle.length; i++) {
graphics.drawRect(rectangle[i][0], rectangle[i][1], 10, 10);
}
graphics.endFill();
stage.addChild(graphics);
renderer.render(stage);
The events are triggered but the object I get by "e" or "this" inside the callback is the object for all graphics. I want to get that single "mouseovered" rectangles object I can see in the graphicsData, but there is no id or anything to identify it by. How can I do this?
Performance is of essence as I'm going to render 20k+ rectangles or circles.
Without drawing each rectangle onto it's own PIXI.Graphics object you won't be able to get individual mouseover events. This is because as far as PIXI is concerned the Graphics object is a single bitmap image.
I would suggest performing your own hit tests inside the mouseover function to detect which rectangle the cursor is over.
If you are using PIXI.Rectangles you can take advantage of the built in Rectangle.Contains function to check if a point (in this case the mouse position) is inside the bounds.

Update fabric.js Path points dynamically

I'm trying to add points to a path object dynamically. When I do, the path renders correctly, but the bounding rectangle never gets updated, making it nearly impossible for a user to select and move the path on canvas.
As you can see in the code below, the path is initially created with a single point, then I dynamically add a second point as well as a control point. After doing this, the bounding rectangle never updates:
var canvas = new fabric.Canvas('c');
canvas.backgroundColor = '#f5f5f5';
var path = new fabric.Path('M 0 20',{
left: 100,
top: 100,
stroke: 'black',
fill: ''
});
canvas.add(path);
var commandArray = [];
commandArray[0] = 'Q';
commandArray[1] = 50;
commandArray[2] = 100;
commandArray[3] = 100;
commandArray[4] = 20;
path.path[1] = commandArray;
canvas.renderAll();
I also tried calling path.setCoords(), but that did not make any difference. How can I get the bounding rectangle to update its dimensions after adding points to a path?
Here's a fiddle: http://jsfiddle.net/flyingL123/17ueLva2/2/
In fabric 3.3.2 I solved it combining the answers above:
var dims = path._calcDimensions()
path.set({
width: dims.width,
height: dims.height,
left: dims.left,
top: dims.top,
pathOffset: {
x: dims.width / 2 + dims.left,
y: dims.height / 2 + dims.top
},
dirty: true
})
path.setCoords()
This correctly updates my path bounding box after adding points like:
path.set({path: points})
I am not sure though, if this works with negative top and left values, but I didn't need that in my case. I guess the main thing is that the _parseDimensions() method was renamed to _calcDimensions().
Please, fabricjs does not support adding point dinamically as of now.
To make it work you can add points like you are doing and then use internal method path._parseDimensions() each time you add points and desire to update bounding box dimension.
var dims = path._parseDimensions();
path.setWidth(dims.width);
path.setHeight(dims.height);
path.pathOffset.x = path.width/2;
path.pathOffset.y = path.height/2;
path.setCoords();
Look this updated fiddle that has the necessary code to solve your problem.
I hope it works for every situation.
http://jsfiddle.net/17ueLva2/6/
It ended up being more complicated. If a point is added to the path that results in _parseDimensions returning a left value that is negative, the path would jump around the screen. For my use case, I need the path to stay in place while points are added and manipulated. This fiddle shows my working solution:
http://jsfiddle.net/flyingL123/8763bx2q/8/
If you run it with a JS console open, you will see the script pausing after each additional point is added, or current point is manipulated. As this happens you will see that the path does not get moved along the canvas, which is the desired behavior. After all the break points complete, you will see that the curve is centered within its selection box.
If there is an easier way to achieve this behavior, I would love to know.
Here's the function I'm using to set the dimensions just in case the fiddle link ever goes away:
function updateDims() {
var dims = path._parseDimensions(),
prevDims = path.prevDims || {},
leftDiff = dims.left - (prevDims.left || 0),
topDiff = dims.top - (prevDims.top || 0);
path.setWidth(dims.width);
path.setHeight(dims.height);
if (dims.left < 0) {
path.pathOffset.x = path.width/2 + dims.left;
path.left = path.left + leftDiff;
} else {
path.pathOffset.x = path.width/2;
}
if (dims.top < 0) {
path.pathOffset.y = path.height/2 + dims.top;
path.top = path.top + topDiff;
} else {
path.pathOffset.y = path.height/2;
}
path.prevDims = dims;
path.setCoords();
}
I couldn't find any new way to do this. But I figured out something like below;
create an SVG path string with modifications.
create a new fabric path object.
replace path, width, height and pathOffset properties of the original path object with the properties of the new path object.
setCoords. renderAll etc...
It may not be much efficient. But it was the solution for me.
var pathObject = new fabric.Path("M0,0 L100,100 ~ Z");
var updatedPath = new fabric.Path("M50,100 L120,46 ~ Z");
pathObject.set({
path : updatedPath.path,
width : updatedPath.width,
height : updatedPath.height,
pathOffset: updatedPath.pathOffset
});
pathObject.setCoords();
On my setup, it says path._parseDimensions is not a function.
I didn't try to solve it. I have to change all path content. So my solution seems better for me :)
Fabric 4.6.0
If you look at constructor of Path, it calls
fabric.Polyline.prototype._setPositionDimensions.call(this, options);
in the end. Where this is your Path and options is your Path constructor second argument. It is enough to call the method above on each path addition/removal to update the position and bounds.

Three.js Enemy Moves towards player

In three.js I have a space ship at xyz, And id like it to fly towards a mesh object of a planet at xyz.
I cannot for the life of me figure this out.
Needs to travel in a straight line, at a speed constant towards the planet.
updateFcts.push(function(delta, now){
if (shipArr[0]===undefined){
}else{
//create two vector objects
var xd = new THREE.Vector3(marsMesh.position.x,marsMesh.position.y,marsMesh.position.z);
var yd = new THREE.Vector3(shipArr[0].position.x,shipArr[0].position.y,shipArr[0].position.z);
//find the distance / hypotnuse to the xyz location
var dicks = shipArr[0].position.distanceTo(marsMesh.position);
var subvec = new THREE.Vector3();
subvec = subvec.subVectors(xd,yd);
//sub subtrac the 3 vectors.
var hypotenuse = dicks;
console.log(hypotenuse);
//1.5 stops it at 1.5 distance from the target planet
if(hypotenuse > 1.5){
//console.log(hypotenuse);
shipArr[0].position.y += .0001*200*(subvec.y/hypotenuse);
shipArr[0].position.x += .0001*200*(subvec.x/hypotenuse);
shipArr[0].position.z += .0001*200*(subvec.z/hypotenuse);
}else{
//within fire range
alert ("FIIIIIRE");
}
}
})
I tried tween.js and was unhappy so i coded a function myself.
You could use https://github.com/sole/tween.js which is focused on that.
A very basic example http://jsfiddle.net/qASPe (square will fly towards sphere after 5s) with mainly this code:
new TWEEN.Tween(ship.position)
.to(planet.position, 700) // destination, duration
.start();
Later, you might want to use a THREE.Curve, or other Path mechanism, as a "flying" path like here http://jsfiddle.net/aevdJ/12
// create a path
var path = new THREE.SplineCurve3([
ship.position,
// some other points maybe? representing your landing/takeoff trajectory
planet.position
]);
new TWEEN.Tween({ distance:0 })
.to({ distance:1 }, 3000) // destination, duration
.onUpdate(function(){
var pathPosition = path.getPointAt(this.distance);
ship.position.set(pathPosition.x, pathPosition.y, pathPosition.z);
})
.start();
In all cases, do not forget to add this line in your update function
TWEEN.update();

Categories