fabric.js - Rotate "Child" Object based on Parent w/o Grouping - javascript

Using fabric.js (1.7.11) , I am trying to have one object follow the rotation of another without grouping them, and while maintaining the relative position of the child object to the parent. So I believe I need to (1) set the rotation center of the "child" object to that of the "parent", then (2) as the parent object is rotating, apply that rotation delta to the child ( which may already start in a rotated state ). The end result is such that the child appears "stuck" to the parent ( like a sticky-note on a game-board, and you then rotate the game-board )
Here is the pseudo-code
canvas.on('object:rotating', canvasEvent_ObjectRotating);
function canvasEvent_ObjectRotating(e)
{
// set r2'2 center of rotation = r1's center of rotation
// r2.angle += ( delta of rotation of r1 angle )
}
Here is a fiddle:
https://jsfiddle.net/emaybert/bkb5awj6/
Any help with 1 or 2 would be appreciated. Either how to get the angle delta in the object:rotating callback, and/or how to pivot a object around an arbitrary point. I've seen the reference to fabric.util.rotatePoint and an example of how to rotate a Line using it, but not an object, and couldn't make that mental transformation.

Is this something that you're trying to do?
https://jsfiddle.net/logie17/ofr8e6qd/1/
function canvasEvent_ObjectRotating(e)
{
origX = r2.getCenterPoint().x;
origY = r2.getCenterPoint().y;
let topLeftPoint = new fabric.Point(origX, origY);
if (!previousAngle) {
previousAngle = r1.getAngle();
}
let angle = fabric.util.degreesToRadians(r1.getAngle() - previousAngle);
previousAngle = r1.getAngle();
let center = r1.getCenterPoint();
let origin = new fabric.Point(r1.left, r1.top);
let newCoords = fabric.util.rotatePoint(topLeftPoint,origin,angle);
r2.set({ left: newCoords.x, top: newCoords.y }).setCoords();
}

Related

How can I avoid overlapping when creating sprites? JS - PHASER3

I'm new in this and I'm making a small game in JS, the problem that I have now is when I create enemies it sometimes overlaps, creating this:
The way that use to create them is simple,
resetShip(enemy_spaceship) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(10,globalThis.config.width);
}
In X each sprite will have a random number from 10 to the width of the screen (canvas), the problem is that if a sprite has 440 in X and another one has 450 in X, those 10px aren't enough to separate them, some people told me to create a grid, but like I said I'm new and searching about grid can't find any example that I can use to this, thanks if you can help me :)
One option is for each enemy ship to be allocated a specific region in which it may start. If you have 2 ships, that means the first ship can be anywhere in the first half of the X axis, and the second ship can be anywhere in the second half of the X axis.
To do this, you should update your resetShip function to also take in a minX and maxX, and use that when defining it's location:
resetShip (enemy_spaceship, minX, maxX) {
enemy_spaceship.y = 0;
enemy_spaceship.x = Phaser.Math.Between(minX, maxX);
}
Then, you need to find a way to rest the group of ships, providing valid regions for each ship. Something like this:
resetEnemies(ships) {
//Each ship may be in a region that is 1/Nth of the width
let regionWidth = globalThis.config.width / ships.length
//We need to know the shipWidth so we don't let ships get too
//close to the left edge.
let shipWidth = 64
ships.forEach((ship, i) => {
//Assuming you just want padding on the left so it is no closer than 10px,
//this will define the minX for the Nth ship
const minX = Math.min(10, i*regionWidth)
//The maxX should not let a ship overlap the next region. So, we subtract the shipWidth
//to ensure that, at worst, it is right next to the next ship
const maxX = (i+1)*regionWidth-shipWidth
//Use the updated restShip to put it in a valid location for it's region
resetShip(ship, minX, maxX)
})
}

Why does fabric resize my object when I move it?

I am implementing snap to canvas edges on the object's scaling event.
In order to snap it to the left edge of the canvas, I have to set the left position to be equal to the canvas' left. I would also have to add to the width the amount I moved the object by.
The odd thing is, just from setting the new left position, sometimes my object shrinks/grows.
Is it because fabric is doing some kind of scaling under the hood? If I add the offset to my width to keep the object at the correct size, the odd behavior becomes more apparent.
threshold = 5;
_fabric.on('object:scaling', handleScaled);
function handleScaled(event) {
const shape = event.target && event.target.shape;
const shapeSize = shape.left + shape.width;
const snapeAmnt = Math.abs(screenSize - shapeSize);
if (Math.abs(screenSize - shapeSize) > threshold) {
shape.width += snapeAmnt;
}
}

anchoring a geomety/mesh to it's corner for scaling three.js

I'm create a rectangle in three.js based on 2 coordinates. The first coordinate is the cell of the first user click, the second coordinate is where the user drags the cursor.
The rectanlge that is being created is the correct size, however it 'grows' from it's center, whereas I want it to 'grow' from the corner of the first user click. I've tried a few potential solutions to change the origin of the geometry but I haven't found a fix yet.
The demo can be see here - with the code below.
var startPoint = startPlace;
var endPoint = endPlace;
var zIntersect = new THREE.Vector3(startPoint.x, 0, endPoint.z);
var xIntersect = new THREE.Vector3(endPoint.x, 0, startPoint.z);
var differenceZ = Math.abs(startPlace.z - zIntersect.z);
var differenceX = Math.abs(startPlace.x - xIntersect.x);
floorGeometryNew.rotateX(-Math.PI / 2);
var floorGeometryNew = new THREE.PlaneGeometry(differenceX, differenceZ);
floorGeometryNew.rotateX(-Math.PI / 2);
var x = startPoint.x;
var y = startPoint.y;
var z = startPoint.z;
var voxel = new THREE.Mesh(floorGeometryNew, tempMaterial);
voxel.position.set(x, y, z);
Center of your rectangle is in the middle between startPoint and endPoint and it's the average of them:
voxel.position.addVectors(startPoint, endPoint).divideScalar(2);
Approach 1. Without creating of a new geometry every time when you change size of a rectangle. The idea is:
Create a mesh of a plane once on start with a double-sided material
Set the first vertex of the plane's geometry with the current point of intersection
Track the point of intersection and apply its value to the last vertex of the plane's geometry and change the second and the third vertices accordingly to positions of the first and the last vertices
For example, we created a plane mesh newRect on mouseDown event and set its geometry's first vertex to the point of intersection which was on that moment:
newRectGeom.vertices[0].set(onPlanePoint.x, onPlanePoint.y + .5, onPlanePoint.z);
and then on mouseMove we get the point of intersection and apply its coordinate to the fourth (last) vertex, also we change values of vertices 1 and 2:
newRect.geometry.vertices[1].set(onPlanePoint.x, newRect.geometry.vertices[0].y, newRect.geometry.vertices[0].z);
newRect.geometry.vertices[2].set(newRect.geometry.vertices[0].x, newRect.geometry.vertices[0].y, onPlanePoint.z);
newRect.geometry.vertices[3].set(onPlanePoint.x, onPlanePoint.y + .5, onPlanePoint.z);
It's simplier than it sounds :)
jsfiddle example. Build mode off - OrbitControls are enabled; Build mode on - controls are disabled, you can draw rectangles.
Approach 2. Instead of controlling vertices we can control position and scaling of rectangle.
On mousedown event we'll set the startPoint with the point of intersection
startPoint.copy(onPlanePoint);
and then we'll find position and scaling of our rectangle:
newRect.position.addVectors(startPoint, onPlanePoint).divideScalar(2);
newRect.position.y = 0.5; // to avoid z-fight
newRect.scale.set(Math.abs(onPlanePoint.x - startPoint.x), 1, Math.abs(onPlanePoint.z - startPoint.z))
jsfiddle example. Visually and functionally it's the same as the Approach 1. From my point of view, Approach 2 is simplier.
When you call
voxel.position.set(x, y, z);
then the center of your mesh is setted to this position. So you have to take half of the length and half of the width of your rectangle to add to this position. These values you can get with a bounding box.
var bbox = new THREE.Box3();
bbox.setFromObject( voxel );
var val = bbox.max.x - bbox.min.x;

Matter.js calculating force needed

Im trying to apply a force to an object. To get it to move in the angle that my mouseposition is generating relative to the object.
I have the angle
targetAngle = Matter.Vector.angle(myBody.pos, mouse.position);
Now I need to apply a force, to get the body to move along that angle.
What do I put in the values below for the applyForce method?
// applyForce(body, position, force)
Body.applyForce(myBody, {
x : ??, y : ??
},{
x:??, y: ?? // how do I derive this force??
});
What do I put in the x and y values here to get the body to move along the angle between the mouse and the body.
To apply a force to move your object in that direction you need to take the sine and cosine of the angle in radians. You'll want to just pass the object's position as the first vector to not apply torque (rotation).
var targetAngle = Matter.Vector.angle(myBody.pos, mouse.position);
var force = 10;
Body.applyForce(myBody, myBody.position, {
x: cos(targetAngle) * force,
y: sin(targetAngle) * force
});
Also if you need it, the docs on applyForce() are here.
(I understand this question is old, I'm more or less doing this for anyone who stumbles across it)
You can rely on the Matter.Vector module and use it to substract, normalize and multiply positions vectors:
var force = 10;
var deltaVector = Matter.Vector.sub(mouse.position, myBody.position);
var normalizedDelta = Matter.Vector.normalise(deltaVector);
var forceVector = Matter.Vector.mult(normalizedDelta, force);
Body.applyForce(myBody, myBody.position, forceVector);

Move Camera to make all objects fit exactly inside the frustum - three.js

EDIT : I have rephrased my question to help users with the same problem.
I have a three.js scene on which I have added some spheres.
I want to move the camera towards a specific direction until all the objects (which are randomly positioned inside the scene) are "fitting exactly" the user's screen.
I have found the answer to my problem!
1. I move the camera (zooming to the desired direction) inside a loop, and in every repeat I create a new frustum using the camera's matrix
2. I check if any of my spheres intersects with a plane of the frustum. If it does, that means that part of one of my objects is outside the frustum so I break the loop and move the camera to its last position.
The above might also works for any object (not only spheres) because every object has a boundingSphere that can be calculated (it might not be very precise the result though).
It also works when zooming out, you 'd just have to move the camera from the object until none of the has a negative distance from all the planes (negative distance means object is "outside" the plane of the frustum).
Code (only for zooming out - r72) :
var finished = false;
var camLookingAt = /* calc. */ ;
while( finished === false ){
var toDirection= camera.position.clone().sub(camLookingAt.clone());
toDirection.setLength(vec.length() - 1); // reduce length for zooming out
camera.position.set(toDirection.x, toDirection.y, toDirection.z);
camera.updateMatrix(); // make sure camera's local matrix is updated
camera.updateMatrixWorld(); // make sure camera's world matrix is updated
var frustum = new THREE.Frustum();
frustum.setFromMatrix( new THREE.Matrix4().multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse ) );
for (var j = frustum.planes.length - 1; j >= 0; j--) {
var p = frustum.planes[j];
for (var i = myMeshSpheres.length - 1; i >= 0; i--) {
var sphere = new THREE.Sphere(myMeshSpheres[0].position.clone(), myMeshSpheres[0].radius);
if( p.distanceToSphere(sphere) < 1 ){ // if is negative means part of sphere is outside plane/frustum
finished = true;
}
}
}

Categories