Fabric.js: Positioning bug when rotating a group? - javascript

When rotating a group in fabric.js, the .left and .top values of the group "jump". Is that a fabric.js bug or somehow explainable/intended?
group.on "moving", ->
#Yields values of about 100 px, also after the group was rotated
group.on "rotating", ->
#Yields values of about 130 px
JSFiddle -> http://jsfiddle.net/thomasf1/X76X9/2/

The difference is due to the originX and originY values changing upon rotation. originX and originY will become 'center' in place of their typical values of 'left' and 'top'. I encountered a similar issue where the positioning would change and found that I needed to be aware of the origin values.

I faced similar problem, but not with group, but single object.
Solution for me was not taking the top and left positions of rotated object, but top left Oocord x and y position.
For example:
fabricPlace.on('object:modified', function(event){
var object = event.target;
var save = {};
save.position = {};
save.id = object.id;
save.position.y = object.oCoords.tl.y;
save.position.x = object.oCoords.tl.x;
save.position.angle = object.angle;
console.log(object);
changeObject(save);
});

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;
}
}

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

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();
}

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);

Raphael Position

How can I get the position of an object in Raphael? I can get the size using getBBox(), but there appears to be no way to get the position?
getBBox() should give you position as well as x and y properties.
var bbox = el.getBBox();
alert([bbox.x, bbox.y]);
getBBox() returns an object with 5 properties. they are:
x
y
width
height
toString()
if you set getBBox( false ) it will return coordinate data for the object's position AFTER a transformation. set it to getBBox( true ) to return coordinates for the object prior to transformation
use like this ...
paper.Raphael(10,10,300,300);
circle.paper( 30, 55, 15 );
var circleBBox = circle.getBBox( false );
edit: just downloaded R 2.1 and i believe it has added x2 and y2 to the properties returned by getBBox()
Depending on what kind of shape it is, the documentation seems to say it can be accessed using the .attr() function. So, if it's a circle...
var x = myCircle.attr('cx'); //cx is the center-x-coordinate of the circle
var y = myCircle.attr('cy'); //same, for y
var r = myCircle.attr('r'); //Radius of circle.
A square would have attrs of x, y, width, height. Check the documentation for more info.
you may also access the x and y values this way:
var x = myCircle.attrs.x;
var y = myCircle.attrs.y
attributes x, y are those within the set. The issue here is that if the set gets translated somewhere else, the x and y given in by .getBBOx() do not account for the translation.
Raphael.transformPath(path, transform) can help by applying the same transforms that the set has...
to translate that point you can:
tp = Raphael.transformPath("M"+x+","+y, set.attr('transform'))
x = tp[0][1]
y = tp[0][2]

Categories