I have found a workaround here (see code below), but I wonder if Fabric has built-in support to set the rotation point of an object to a specific place.
function rotateObject(fabObj, angleRadian, pivotX, pivotY) {
ty = pivotY - fabObj.height / 2.0;
tx = pivotX - fabObj.width / 2.0;
if (angleRadian >= Math.PI * 2) {
angleRadian -= Math.PI * 2;
}
angle2 = Math.atan2(ty, tx);
angle3 = (2 * angle2 + angleRadian - Math.PI) / 2.0;
pdist_sq = tx * tx + ty * ty;
disp = Math.sqrt(2 * pdist_sq * (1 - Math.cos(angleRadian)));
fabObj.set({transformMatrix:[
Math.cos(angleRadian),
Math.sin(angleRadian),
-Math.sin(angleRadian),
Math.cos(angleRadian),
disp * Math.cos(angle3),
disp * Math.sin(angle3)
]});
}
There is no built in method.
By default mouse rotation on fabricjs is around the center point.
If you deactivate centeredRotation on objects, setting it to false:
fabric.Object.prototype.centeredRotation = false
the object will start to rotate around the originX and originY position.
Then you can set for each object a specific originX and originY, that can be numeric and represent any point inside the object ( with values ranging from 0 to 1 ) i m not even sure if you can do it with points outside the object.
At that point the object rotates around that point, just setting the angle property to a desired angle.
Consider that now also position is relative to that origin.
As a side not, do not set transformMatrix of an object. is unsupported and will give you weird controls in case of interactivity with controls.
In your specific example, once found the matrix:
var matrix = [
Math.cos(angleRadian),
Math.sin(angleRadian),
-Math.sin(angleRadian),
Math.cos(angleRadian),
disp * Math.cos(angle3),
disp * Math.sin(angle3)
];
var options = fabric.util.qrDecompose(matrix);
object.set(options);
object.setPositionByOrigin({ x: options.translateX, y: options.translateY }, 'center', 'center');
this should give you same effect but being supported by fabricjs better.
Rotation on fabricjs is around the center point.
This fuction is on developmenting...
but I have implemented to line rotation.
Redrawing line from custom center point to mouse position.
it works well for me.
Related
I'm attempting to write code that will generate fractals according to the Chaos game
In particular, I'm trying to debug the faulty generation/rendering of this fractal:
I'm doing this with Javascript in a Canvas element. The relevant Javascript is below:
canvas = document.getElementById('myCanvas');
context = canvas.getContext('2d');
//constants
border = 10 //cardinal distance between vertices and nearest edge(s)
class Point{
constructor(_x, _y){
this.x = _x;
this.y = _y;
}
}
vertices = []
secondLastVertex = 0;
lastVertex = 0;
//vertices in clockwise order (for ease of checking adjacency)
vertices.push(new Point(canvas.width / 2, border)); //top
vertices.push(new Point(canvas.width - border, canvas.height * Math.tan(36 * Math.PI / 180) / 2)); //upper right
vertices.push(new Point(canvas.width * Math.cos(36 * Math.PI / 180), canvas.height - border)); //lower right
vertices.push(new Point(canvas.width * (1 - (Math.cos(36 * Math.PI / 180))), canvas.height - border)); //lower left
vertices.push(new Point(border, canvas.height * Math.tan(36 * Math.PI / 180) / 2)); //upper left
//move half distance towards random vertex but it can't neighbor the last one IF the last two were the same
function updatePoint(){
//pick a random vertex
v = Math.floor(Math.random() * vertices.length);
if(lastVertex == secondLastVertex)
//while randomly selected vertex is adjacent to the last approached vertex
while((v == (lastVertex - 1) % 5) || (v == (lastVertex + 1) % 5))
//pick another random vertex
v = Math.floor(Math.random() * vertices.length);
//cycle the last two vertices
secondLastVertex = lastVertex;
lastVertex = v;
//move half way towards the chosen vertex
point.x = (vertices[v].x + point.x) / 2;
point.y = (vertices[v].y + point.y) / 2;
}
//starting point (doesn't matter where)
point = new Point(canvas.width / 2, canvas.height / 2);
for (var i = 0; i < 1000000; i++){
//get point's next location
updatePoint();
//draw the point
context.fillRect(Math.round(point.x), Math.round(point.y), 1, 1);
}
The rendering that is produced looks like this:
So far I haven't been able to determine what is causing the rendering to be so skewed and wrong. One possibility is that I've misunderstood the rules that generate this fractal (i.e. "move half the distance from the current position towards a random vertex that is not adjacent to the last vertex IF the last two vertices were the same")
Another is that I have some bug in how I'm drawing fractals. But the same code with rule/starting-vertex modifications is able to draw things like the Sierpinkski triangle/carpet and even other pentagonal fractals apparently perfectly. Though one other pentagonal fractal ended up with some weird skewing and "lower right fourth of each self-similar substructure" weirdness.
I tried making some slight modifications to how I interpreted the rules (e.g. "next vertex can't be adjacent OR EQUAL TO previous vertex if last two vertices were the same") but nothing like that helped. I also tried not rounding the coordinates of the target point before plotting it, but though this slightly changed the character/sharpness of the details, it didn't change any larger scale features of the plot.
My issue as kindly pointed out by ggorlen, was that I wasn't comparing vertices for adjacency correctly. I mistakenly thought Javascript evaluated something like (-1 % 5) as 4, rather than -1.
To fix this, I add 4 to the index instead of subtracting 1, before modding it against 5 (the number of vertices)
This completely fixed the render. (in not just this case but other cases I'd been testing with different fractals)
I'm trying to use the answer provided here: Intersection of two Moving Objects with Latitude/Longitude Coordinates
But I have some questions..
What is this angle:
var angle = Math.PI + dir - target.dir
I was thinking that the angle that should be used in the law of cosines is already "alpha or target.dir".. What is that line doing? Also in these two steps:
var x = target.x + target.vel * time * Math.cos(target.dir);
var y = target.y + target.vel * time * Math.sin(target.dir);
Shouldn't the code be using the angle between x- or y-axis and the target velocity vector? Why is the author using alpha here?
What is this angle:
var angle = Math.PI + dir - target.dir
The variable named angle is indeed the angle alpha. Because the direction dir is the direction from chaser to target, and we need it the other way round for this calculation, we add π to it before we subtract target.dir.
Maybe using the word angle as a variable name was a bit vague; I'll change it to alpha, the name I used for this angle in the images.
Shouldn't the code be using the angle between x- or y-axis and the target velocity vector? Why is the author using alpha here?
var x = target.x + target.vel * time * Math.cos(target.dir);
var y = target.y + target.vel * time * Math.sin(target.dir);
We are indeed using target.dir, which is the direction of the target, i.e. the angle between the x-axis and the target vector, to calculate the coordinates of the interception point, and not the angle alpha.
I'm making a game in javascript, where an object is supposed to bounce from walls. I really tried to get it to work myself, but it never works correctly.
Let's say theres a ball bouncing inside this cage (blue = 30°, brown = 60°);
The ball's coordinates are known. The angle of movement is known. The point of collision (P) coordinates are known. The angle of the wall is known. The ball's position is updating it's coordinates inside a setInterval function using this function:
function findNewPoint(x, y, angle, distance) {
var result = {};
result.x =(Math.cos(angle * Math.PI / 180) * distance + x);
result.y = (Math.sin(angle * Math.PI / 180) * distance + y);
return result;
So, upon colliding, there should be a function that properly changes the ball's angle. It's a very complicated problem it seems, because even if I know that the wall is 30°, its important to know from what side the ball is colliding into it. I tried using the "Reflection across a line in the plane" formula and also some vectors, but it never worked out for me. I'm not expecting a complete answer with code, if someone could suggest in what way this should be programmed, it would help aswell.
Edit:
Thanks for your tips guys, I realized what was causing the most confustion; if I select an angle on the canvas with my mouse, the starting coordinate(0,0) is in the bottom left corner. But since the canvas' starting coordinate is in the top left corner, this has to be considered.
Basically using this formula for calculating the angle:
function angle(cx, cy, ex, ey) {
var dy = ey - cy;
var dx = ex - cx;
var theta = Math.atan2(dy, dx);
theta *= 180 / Math.PI;
return theta;
}
if the ball moved from (50,50) to (100,100), the angle would be -45.
Now, this angle changes in the following way when hitting walls:
If im honest, I got these out of trial and error, am not really understanding why exactly 60 and 120.
It is not wise to use angle for moving ball and calculate Cos/Sin again and again. Instead use unit velocity direction vector with components vx, vy like this:
new_x = old_x + vx * Velocity_Magnitude * Time_Interval
Note that vx = Cos(angle), vy = Sin(angle), but with direction approach you seldom need to use trigonometric functions.
Tilted wall with angle Fi has normal
nx = -Sin(Fi)
ny = Cos(Fi)
To find reflection , you need to calculate dot product of velocity and normal
dot = vx * nx + vy * ny
Velocity after reflection transforms:
vnewx = v.x - 2 * dot * n.x
vnewy = v.y - 2 * dot * n.y
Use these values for further moving
(note that you can use both internal and external normal direction, because direction flip changes both components, and sign of 2 * dot * n.x remains the same)
Examples:
horizontal moving right
vx=1, vy=0
30 degrees wall has normal
nx=-1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = 1 - 2 * (-1/2) * (-1/2) = 1/2
vnewy = 0 - 2 * (-1/2) * Sqrt(3)/2 = Sqrt(3)/2
(velocity direction angle becomes 60 degrees)
horizontal moving left
vx=-1, vy=0
330 degrees wall (left bottom corner) has normal
nx=1/2, ny=Sqrt(3)/2
dot = -1/2
vnewx = -1 - 2 * (-1/2) * (1/2) = -1/2
vnewy = 0 - 2 * (-1/2) * (Sqrt(3)/2) = Sqrt(3)/2
(velocity direction angle becomes 120 degrees)
Here is a function that returns the angle of reflection given an angle of incidence and a surface angle (in degrees). It also ensures that the returned angle is between 0 and 359 degrees.
function angleReflect(incidenceAngle, surfaceAngle){
var a = surfaceAngle * 2 - incidenceAngle;
return a >= 360 ? a - 360 : a < 0 ? a + 360 : a;
}
Here's a demonstration, where the blue line is the angle of incidence, the purple line is the angle of reflection, and the black line is the surface.
If you're assuming that the ball behaves like light bouncing off a mirror, then the angle of incidence equals the angle of reflection.
So your board is 30° from 0° (straight up). The means the normal (perpendicular to the board at the point the ball hits ) is 300°. Say the ball arrives from 280°, it must leave at 320° as the difference between the angle of incidence and the normal and the angle of reflection and the normal must be equal.
I am struggling with connecting two circles with a line. I am using the famo.us library.
DEMO on Codepen
a.k.a. "Two balls, one line."
The Problem
Angle and length of the line are correct, but the position is wrong.
First attempt
The important part should be lines 114-116:
connection.origin = [.5, .5];
connection.align = [.5, .5];
connection.body.setPosition([
Math.min(sourcePos.x, targetPos.x),
Math.min(sourcePos.y, targetPos.y)
]);
Appearently i am doing something wrong with the math. Playing around with those values gives me all kinds of results, but nothing is close to correct.
Intended solution
(1) The minimal solution would be to connect the centres of the circles with the line.
(2) The better solution would be a line that is only touching the surface of both circles instead of going to the center.
(3) The ideal solution would have arrows on each end of the line to look like a directed graph.
This fixes it :
connection.body.setPosition([
sourcePos.x * Math.cos(angle) + sourcePos.y * Math.sin(angle),
sourcePos.x * Math.sin(-angle)+ sourcePos.y * Math.cos(angle)
]);
Your segment is defined by its extrimity in sourceand the angle and distance to target, thus you have to set its origin to be that of source
The rotation seems to not only rotate the object, but also rotate the coordinates around the origin, so I rotated them by -angle to compensate.
There might be a more famo.usesque way to do it (maybe you can get it to rotate before setting the position, or have the position be 0,0 and add the coordinates as a translation in the transformation).
To get your better solution, still with mostly math, you may keep the same code but
with r the radius of the source ball, remove [r * distX / distance, r * distY / distance] to the coordinates of the segment, to put it in contact with the outer part of the ball
remove both balls' radius from the distance
With that, we get :
var distX = sourcePos.x - targetPos.x;
var distY = sourcePos.y - targetPos.y;
var norm = Math.sqrt(distX * distX + distY * distY);
var distance = norm - (source.size[0]+target.size[0])/2;
var angle = -Math.atan2(-distY, distX);
connection.angle = angle;
connection.size = [distance, 2, 0];
connection.align = [.5, .5];
connection.origin = [.5, .5];
var posX = sourcePos.x - source.size[0]/2 * (distX / norm);
var posY = sourcePos.y - source.size[0]/2 * (distY / norm);
connection.body.setPosition([
posX * Math.cos(angle) + posY * Math.sin(angle),
posX * Math.sin(-angle)+ posY * Math.cos(angle)
]);
result on this fork : http://codepen.io/anon/pen/qEjPLg
I think the fact that the line length is off when the balls go fast is a timing issue. Most probably you compute the segment's length and position at a moment when the ball's centres are not yet updated for that frame.
I'm trying to write a small 'perspective' javascript app that allows me to fly through a set of x,y,z points that inhabit a 3d space.
I have the concept of a camera which changes its rotation and xyz position, while each point maintains a constant xyz point.
I then have a set of equations that works out how the camera's x,y,z coordinates should be adjusted for flying directly forwards. The x,y,z adjustments obviously depend upon the rotation of the camera.
It almost works, but at certain 'attitudes' the camera position adjustment goes wrong and the flightpath doesn't go straight ahead but goes off at an angle, or even reverses. The equations for working out the projection are as follows:
var directionFactor = 1;
if (direction == 'backward') directionFactor = -1;
sx = Math.sin(cameraView.rotX);
cx = Math.cos(cameraView.rotX);
sy = Math.sin(cameraView.rotY);
cy = Math.cos(cameraView.rotY);
sz = Math.sin(cameraView.rotZ);
cz = Math.cos(cameraView.rotZ);
// Z-Axis
ztrig = Math.sqrt((cx * cx) + (cy * cy)) * (cx * cy);
cameraView.z = cameraView.z + directionFactor *
(Math.abs(airspeed / 15) * ztrig);
// Y-Axis
ytrig = Math.sqrt((sx * sx) + (cz * cz)) * (sx * cz);
cameraView.y = cameraView.y + directionFactor *
(Math.abs(airspeed / 15) *ytrig);
// X-Axis
xtrig = Math.sqrt((cz * cz) + (sy * sy)) * (cz * sy);
cameraView.x = cameraView.x - directionFactor *
(Math.abs(airspeed / 15) * xtrig);
Obviously my equations aren't quite right. Can anyone tell me where I'm going wrong? Much appreciated and thanks.
You have some errors in your equations. (They are valid in the 2d case but not in 3d)
when you calculate
sx = Math.sin(cameraView.rotX);
It does make sense in 2d since :
sx = Math.sin(cameraView.rotX) = x/SQRT(y*y + x*x)
where (x, y) is the position of the camera.
But in 3d it's more complicated :
In 3d :
Thus to obtain the cartesian coordinate :
You may also use 3d matrix to perform rotation.