FabricJs and Polygon transformed coordinates - javascript

We're new to Fabric.js. We're using it to draw polygons on our canvas in overlay on a real scene. We need to move, to rotate and resize the polygon and then get back the veterxes coordinates.
After the transformation we used the API.
var originalPolyPoints = pol.get('points').map(function (p)
{
return {x: p.x , y: p.y };
});
This way, the coordinates are always the original but not the changed ones.
How can we get the new coordinates?
Tried to get the transformMatrix of the polygon, but it was null. Even if we have the matrix, how can we apply it?

Answer is simple, just check code below:
var polygon = new fabric.Polygon(
[
{ x: 0, y: 0 },
{ x: 0, y: 100 },
{ x: 100, y: 100 },
{ x: 100, y: 0 },
{ x: 100, y: 200 },
],
{
fill: "",
stroke: "blue",
strokeWidth: 2,
}
);
polygon.on("modified", function () {
var matrix = this.calcTransformMatrix();
var transformedPoints = this.get("points")
.map(function (p) {
return new fabric.Point(
p.x - polygon.pathOffset.x,
p.y - polygon.pathOffset.y
);
})
.map(function (p) {
return fabric.util.transformPoint(p, matrix);
});
console.log(transformedPoints);
});
It has worked for me, hope works for you also:) .
Ref: https://stackoverflow.com/a/53710375/15481471

I think maybe you need to call setCoords on the objects after you modify them. This is a purposeful abstraction leak for performance reasons, according to the documentation.

The point is that all polygon's points have absolute coordinates to the canvas. In order to get actual points coordinates, you have to perform some transformation. It's required because there could be some movements and/or transformations applied to the polygon.
Check out the following answer: https://stackoverflow.com/a/53710375/4681279

Related

Three.js - rotating a camera around a point

I have my custom Navigation control similar to Trackball but with additional functionality.
One of the features I want to implement is the specifying center of rotation of the camera and when we pan a camera, the rotation maintained around the specified point.
Here is the code of my update() function:
update(): this {
const camera = this.camera
if (this._enabled && camera && this._isInvalid) {
const cameraObject = camera.object
// validate all navigation changes
{
const {
offset,
panOffset,
axis,
tempVector3,
tempSpherical,
deltaRotation,
deltaPan,
quaternion
} = this.computeHelpers
// offset vector from eye to target
offset.subVectors(this._eye, this._target)
// apply rotation
if (deltaRotation.isInvalid) {
const { x: theta, y: phi } = deltaRotation.get()
// rotate around screen-space y-axis
if (theta) {
axis.set(0, 1, 0).applyQuaternion(cameraObject.quaternion)
quaternion.setFromAxisAngle(axis, theta)
offset.applyQuaternion(quaternion)
this._up.applyQuaternion(quaternion)
}
// rotate around screen-space x-axis
if (phi) {
axis.set(1, 0, 0).applyQuaternion(cameraObject.quaternion)
quaternion.setFromAxisAngle(axis, phi)
offset.applyQuaternion(quaternion)
this._up.applyQuaternion(quaternion)
}
// mark compute objects as valid
deltaRotation.set({ x: 0, y: 0 })
deltaRotation.isInvalid = false
}
// apply panning
if (deltaPan.isInvalid) {
cameraObject.updateMatrix()
const matrix = cameraObject.matrix
const { x: deltaPanX, y: deltaPanY } = deltaPan.get()
// pan screen space x-direction
{
tempVector3
// get x-column of local transform matrix
.setFromMatrixColumn(matrix, 0)
.multiplyScalar(deltaPanX)
panOffset.add(tempVector3)
}
// pan screen space y-direction
{
tempVector3
// get y-column of local transform matrix
.setFromMatrixColumn(matrix, 1)
.multiplyScalar(-deltaPanY)
panOffset.add(tempVector3)
}
this._target.add(panOffset)
// mark compute objects as valid
panOffset.set(0, 0, 0)
deltaPan.set({ x: 0, y: 0 })
deltaPan.isInvalid = false
}
// assemble new eye (camera) position
this._eye.addVectors(offset, this._target)
this._eyeSpherical.setFromVector3(this._eye)
this._direction.copy(this._eye).normalize()
}
// update camera position
cameraObject.position.copy(this._eye)
cameraObject.up.copy(this._up)
cameraObject.lookAt(this._target)
// mark the controller as valid
this._isInvalid = false
}
return this
}
How can I apply the translation?
Expected result:
Thanks a lot in advance

Highcharts Annotation SVG doesn't move with other shapes

in my Highcharts project, I successfully make all the shapes in a single annotation draggable, but when I add the SVG (the type of this shape is "path"), the SVG doesn't move along with other shapes in the annotation.
I need to have some customised shapes in the annotation. Can anyone point out what the issue is for this SVG? Is it possible to put the SVG within annotation and still be draggable? Or is it some mistake I made that causes the issue?
My example here. https://jsfiddle.net/roy_jaide/754xuhtk/ As you can see, the label, circle and the line are all draggable, but the SVG shape just doesn't move at all.
Thanks for reading my question, and much appreciated if any solution provided.
Highcharts.Annotation.prototype.onDrag = function (e) {
if (this.chart.isInsidePlot(e.chartX - this.chart.plotLeft, e.chartY - this.chart.plotTop)) {
var translation = this.mouseMoveToTranslation(e),
xAxis = this.chart.xAxis[0],
yAxis = this.chart.yAxis[0],
xStep = this.options.stepX,
yStep = this.options.stepY,
pxStep = xAxis.toPixels(xStep) - xAxis.toPixels(0),
pyStep = yAxis.toPixels(yStep) - yAxis.toPixels(0);
if (this.options.draggable === 'x') { //for now, it's exclusive for age handle
this.potentialTranslationX += translation.x;
if (Math.abs(this.potentialTranslationX) >= Math.abs(pxStep)) {
translation.x = (this.potentialTranslationX > 0) ? pxStep : -pxStep;
translation.y = 0;
this.currentXValue += (this.potentialTranslationX > 0) ? xStep : -xStep;
this.potentialTranslationX -= (this.potentialTranslationX > 0) ? pxStep : -pxStep; //minus the step and continue to cumulate
//this.potentialTranslation = 0; //not correct, the mouse will go faster than the handle
if (this.points.length) {
this.translate(translation.x, 0);
} else {
this.shapes.forEach(function (shape) {
shape.translate(translation.x, 0);
});
this.labels.forEach(function (label) {
label.translate(translation.x, 0);
label.text = label.annotation.options.preText + label.annotation.currentXValue;
});
}
}
}
this.redraw(false);
}
}
Update 1: After trying Sebastian's answer on my chart, it turns out to be difficult to calculate the correct coordinates. At last I use type "image" to put display the shape. The shape is a Font-Awesome icon so before using "image" I did try to add a label with "useHTML" : true, but it seems the icon is moved a little after the first redraw(false), not sure why.
The image of the shape. I achieved this by adding "image" shape.
d: ["M", 440, 72, "L", 410, 45, 470, 45, "Z"],
I wouldn't recommend this way to create a draggable custom shape. This option creates a shape as expected but also sets a fixed position. Probably it is possible to implement move functionality, but I think that it will require a lot of changes into draggable core code.
What I can suggest is to make it this way:
annotations: [{
draggable: 'x',
shapes: [{
points: [{
x: 440,
y: 72
}, {
x: 410,
y: 45
}, {
x: 470,
y: 45
}, {
x: 440,
y: 72
}],
fill: 'red',
type: 'path',
stroke: "blue",
strokeWidth: 3,
markerStart: 'circle',
}]
}]
Where markerStart is a defined shape.
See a Demo

Using easelJS to free transform shapes on pressmove

I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/

Countdown animation with EaselJs

I am trying to simulate a countdown animation using easeljs. I have got an example of what it should look like. http://jsfiddle.net/eguneys/AeK28/
But it looks like a hack, is there a proper/better/flexible way to do this?
To put it other way, how can i define a path, and draw that path with easeljs.
This looks ugly:
createjs.Tween.get(bar, {override: true}).to({x: 400}, 1500, createjs.linear)
.call(function() {
createjs.Tween.get(bar, {override: true}).to({y: 400}, 1500, createjs.linear)
.call(function() {
createjs.Tween.get(bar, {override: true}).to({ x: 10 }, 1500, createjs.linear)
.call(function() {
createjs.Tween.get(bar, {override: true}).to({ y: 10 }, 1500, createjs.linear);
})
});
});
You can use the TweenJS MotionGuidePlugin to tween along a path instead of having multiple tweens.
createjs.MotionGuidePlugin.install();
createjs.Tween.get(bar).to({
guide: {path: [10,10, 10,10,400,10, 400,10,400,400, 400,400,10,400, 10,400,10,10]}
}, 6000, createjs.linear);
The path array is basically the set of coordinates for a moveTo call followed by multiple curveTo calls. The coordinates will be interpolated along the path resulting from those calls.
A more modular way to to specify your path array would be to have a function generating it using a set of points you declared.
function getMotionPathFromPoints (points) {
var i, motionPath;
for (i = 0, motionPath = []; i < points.length; ++i) {
if (i === 0) {
motionPath.push(points[i].x, points[i].y);
} else {
motionPath.push(points[i - 1].x, points[i - 1].y, points[i].x, points[i].y);
}
}
return motionPath;
}
var points = [
new createjs.Point(10, 10),
new createjs.Point(400, 10),
new createjs.Point(400, 400),
new createjs.Point(10, 400),
new createjs.Point(10, 10)
];
createjs.MotionGuidePlugin.install();
createjs.Tween.get(bar).to({
guide: {path: getMotionPathFromPoints(points)}
}, 6000, createjs.linear);
FIDDLE

PhysicsJS - how to create a body which rotates about a fixed point when struck?

I've been experimenting with the excellent PhysicsJS library, especially constraints and collisions. What I'm trying to achieve is a polygon which can spin about a fixed rotation point. As a real world example, imagine a wide shelf nailed to a wall with just one long nail through the very center, so that it spins round maybe half or one revolution when something off-center drops on it from above.
Here is an example fiddle: http://jsfiddle.net/2HRGW/41/
and the code:
Physics(function (world) {
var renderer = Physics.renderer('canvas', {
el: 'viewport',
width: 500,
height: 400
});
world.add(renderer);
var nail = Physics.body('circle', {
x: 250,
y: 200,
radius: 5,
mass: 1,
fixed: true
});
world.add(nail);
var shelf = Physics.body('convex-polygon', {
x: 250,
y: 200,
vertices: [{
x: -100,
y: -10
}, {
x: 100,
y: -10
}, {
x: 100,
y: 10
}, {
x: -100,
y: 10
}],
mass: 100,
restitution: 0.5
});
world.add(shelf);
var ball = Physics.body('circle', {
x: 175,
y: 50,
radius: 20,
mass: 10
});
world.add(ball);
world.add(Physics.integrator('verlet', {
drag: 0.003
}));
var verletConstraints = Physics.behavior('verlet-constraints', {
iterations: 2
});
verletConstraints.distanceConstraint(shelf, nail, 1);
world.add(verletConstraints);
world.add(Physics.behavior('constant-acceleration'));
world.add(Physics.behavior('body-collision-detection'));
world.add(Physics.behavior('body-impulse-response'));
world.add(Physics.behavior('sweep-prune'));
world.add(Physics.behavior('verlet-constraints'));
var bounds = Physics.aabb(0, 0, 500, 400);
world.add(Physics.behavior('edge-collision-detection', {
aabb: bounds,
restitution: 0.01
}));
Physics.util.ticker.subscribe(function (time, dt) {
world.step(time);
});
world.render();
Physics.util.ticker.start();
world.subscribe('step', function () {
world.render();
});
});
I define a fixed circle for the nail and a non-fixed polygon for the shelf and add a distance constraint linking the polygon to the circle. As you can see, there are 2 problems. Firstly, the shelf immediately drops down slightly until its top edge is flush with the top of the nail, rather than remaining evenly positioned around the nail. Secondly, when the ball drops onto the shelf, the shelf goes crazy spinning round endlessly, despite having mass and various restitution settings tried. Adjusting its position slightly, sometimes it can even detach completely and fly off.
Am I on the right track using constraints in this way, or is there a simpler solution?
As other users have mentioned, this is a current limitation of PhysicsJS. It's being worked out:
scoping of collisions https://github.com/wellcaffeinated/PhysicsJS/issues/30
constraints https://github.com/wellcaffeinated/PhysicsJS/issues/5
In the mean time, instead of patching the library, why not create a custom pin constraint behavior. For a simple pin constraint behavior that pins the body centroid to a target position, it's quite easy. Here's a jsFiddle of your example with a custom pin constraint manager defined at the beginning.
http://jsfiddle.net/wellcaffeinated/2HRGW/50/
// create a behavior to handle pin constraints
Physics.behavior('pin-constraints', function( parent ){
return {
init: function( opts ){
parent.init.call( this, opts );
this.pins = [];
},
add: function( body, targetPos ){
this.pins.push({
body: body,
target: Physics.vector( targetPos )
});
},
behave: function( data ){
var pins = this.pins
,pin
;
for (var i = 0, l = pins.length; i < l; i++){
pin = pins[ i ];
// move body to correct position
pin.body.state.pos.clone( pin.target );
}
}
};
});
Your overall implementation is correct.
The only thing you got wrong is the mass of the convex-polygon, 100 is a wild value.
A mass of around 0.2 should be enough for that object.

Categories