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;
}
}
Related
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();
}
I am currently working on a game that has to do with a rocket ship moving around and objects(circles) are falling from the top. The goal of this game is to not hit the objects as they are falling down the screen. I am running into problems when writing my collision algorithm.
I have declared var hit = false; at the top of my code
I have also put all of the circles into an array called projectiles.
I believe that I have the logic correct but I discovered that when calling either p.width or ship.width it returns NaN. I have tried using offsetWidth and that didn't work either. I am wondering how else to go about getting the width of my objects
The else statement at the bottom is just to check if .width is returning the correct number. Once I get it to work it will be removed and replaced with the final parts of the collision algorithm.
function checkCollision()
{
for (i = 0; i < projectiles.length; i++) {
var p = projectiles[i];
if((p.x + p.width) < ship.x)
{
hit = false;
}
else if(p.x > (ship.x + ship.width))
{
hit = false;
}
else if(p.y > (ship.y + ship.height))
{
hit = false;
}
else if((p.y + p.height) < ship.y)
{
hit = false;
}
else {
console.log(ship.x + ship.width);
}
In the documentation for createjs.Bitmap there do not exist properties for .width and .height. Instead access it's .image (HTMLImageElement) property which have defined width and height properties: ship.image.width and ship.image.height.
If your object is an EaselJS Bitmap, you can retrieve the physical size using getBounds(). This method works for some EaselJS display objects (ie, not Shapes, and accuracy varies with Text).
var bounds = ship.getBounds();
var w = bounds.width;
Notes:
per #Spencer's message, you can access the .image, and get the width/height, but it will be the original size of the image, so if you transform the bitmap instance, or any of the parent containers, the value will be wrong. The getBounds will consider the scale transformation (if it exists)
values may not be correct if the item is rotated.
bounds are based on the registration point, so the x/y might be non-zero.
You will get 0 for width/height if the image is not yet loaded
For your projectile, if it is a shape, the bounds will always be null, but you can manually set them if you know them, and they will be properly calculated/transformed:
var p = new createjs.Shape();
p.graphics.beginFill("red").drawCircle(0,0,20);
p.setBounds(new createjs.Rectangle(-20,-20,40,40));
Here is some info on why there is no .width or .height: http://blog.createjs.com/update-width-height-in-easeljs/
I am attempting to distort an image displayed within a canvas, so it looks like a "planet". However I am struggling to find away to deal with a distortion issue. The only solution coming to mind is to find a way to reduce the radiusDistance variable, the bigger it is. That said, I am unsure how to achieve this. Any suggestions?
Below is the math and objects I am currently using to achieve this:
polarArray = [];
var polar = function(a,r,c){ //polar object, similar to pixel object.
this.a = a; //angle
this.r = r; //radius (distance)
this.color = c; //color, stored using an object containg r,g,b,a variables
};
loopAllPixels(function(loop){//loop through every pixel, stored in a pixel array
var pixel = loop.pixel;
/*each pixel object is structured like this:
pixel {
x,
y,
color {
r,
g,
b,
a
}
}
*/
var angle = pixel.x/360*Math.PI;
var radiusDistance = pixel.y/Math.PI;
polarArray.push(new polar(angle,radiusDistance,pixel.color));//store polar coordinate pixel + colour.
pixel.color = new colorRGBA(255,255,255,255);//set background as white.
});
for (var i=0;i<polarArray.length;i++){//loop through polarArray (polar coordinate pixels + colour)
var p = polarArray[i]; //polar pixel
var x = (p.r*Math.cos(p.a))+(canvas.width/2); //x coordinate
var y = (p.r*Math.sin(p.a))+(canvas.height/2); //y coordinate
if (setpixel(x,y,p.color)==true){ //set pixel at location.
continue;
}
break;
}
updatePixelsToContext();//draw to canvas
And here is the effect it currently produces (note that I flip the image horizontally before applying it to the canvas, and in this example, I set the background with a magenta kind of colour, for better clarity of the issue):
Note:
I am intending for the warping effect, just not the "ripping" of the pixels, caused by not obtaining all the neccessary pixel data required.
Also bear in mind that speed and effeciency isn't my priority here as of yet.
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;
}
}
}
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);
});