By default all shapes stretch symmetrically from the center of the shape. Is it possible to stretch a shape to specific side?
Things "stretch" from their origin point. If you draw from the center, and then scale, then it will appear to scale from the center.
Draw rectangle from center
var r1 = new createjs.Shape();
r1.graphics.beginStroke("red").drawRect(-100,-100,200,200);
Draw rectangle from left
var r2 = new createjs.Shape();
r2.graphics.beginStroke("red").drawRect(0,0,200,200);
Here is a fiddle
https://jsfiddle.net/owx26481/
Alternately you can change the registration point, which basically offsets where the object is drawn from, and has the same effect:
var r1 = new createjs.Shape();
r1.graphics.beginStroke("red").drawRect(0,0,200,200);
var r2 = new createjs.Shape();
r2.graphics.beginStroke("red").drawRect(0,0,200,200); // SAME
r2.regX = r2.regY = 100; // Change registration point to the center (50%)
Here is an updated fiddle: https://jsfiddle.net/owx26481/2
I hope that makes sense!
Related
What I'm trying to achieve is a rotation of the geometry around pivot point and make that the new definition of the geometry. I do not want te keep editing the rotationZ but I want to have the current rotationZ to be the new rotationZ 0.
This way when I create a new rotation task, it will start from the new given pivot point and the newly given rad.
What I've tried, but then the rotation point moves:
// Add cube to do calculations
var box = new THREE.Box3().setFromObject( o );
var size = box.getSize();
var offsetZ = size.z / 2;
o.geometry.translate(0, -offsetZ, 0)
// Do ratation
o.rotateZ(CalcUtils.degreeToRad(degree));
o.geometry.translate(0, offsetZ, 0)
I also tried to add a Group and rotate that group and then remove the group. But I need to keep the rotation without all the extra objects. The code I created
var box = new THREE.Box3().setFromObject( o );
var size = box.size();
var geometry = new THREE.BoxGeometry( 20, 20, 20 );
var material = new THREE.MeshBasicMaterial( { color: 0xcc0000 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.x = o.position.x;
cube.position.y = 0; // Height / 2
cube.position.z = -size.z / 2;
o.position.x = 0;
o.position.y = 0;
o.position.z = size.z / 2;
cube.add(o);
scene.add(cube);
// Do ratation
cube.rotateY(CalcUtils.degreeToRad(degree));
// Remove cube, and go back to single object
var position = o.getWorldPosition();
scene.add(o)
scene.remove(cube);
console.log(o);
o.position.x = position.x;
o.position.y = position.y;
o.position.z = position.z;
So my question, how do I save the current rotation as the new 0 rotation point. Make the rotation final
EDIT
I added an image of what I want to do. The object is green. I have a 0 point of the world (black). I have a 0 point of the object (red). And I have rotation point (blue).
How can I rotate the object around the blue point?
I wouldn't recommend updating the vertices, because you'll run into trouble with the normals (unless you keep them up-to-date, too). Basically, it's a lot of hassle to perform an action for which the transformation matrices were intended.
You came pretty close by translating, rotating, and un-translating, so you were on the right track. There are some built-in methods which can help make this super easy.
// obj - your object (THREE.Object3D or derived)
// point - the point of rotation (THREE.Vector3)
// axis - the axis of rotation (normalized THREE.Vector3)
// theta - radian value of rotation
// pointIsWorld - boolean indicating the point is in world coordinates (default = false)
function rotateAboutPoint(obj, point, axis, theta, pointIsWorld){
pointIsWorld = (pointIsWorld === undefined)? false : pointIsWorld;
if(pointIsWorld){
obj.parent.localToWorld(obj.position); // compensate for world coordinate
}
obj.position.sub(point); // remove the offset
obj.position.applyAxisAngle(axis, theta); // rotate the POSITION
obj.position.add(point); // re-add the offset
if(pointIsWorld){
obj.parent.worldToLocal(obj.position); // undo world coordinates compensation
}
obj.rotateOnAxis(axis, theta); // rotate the OBJECT
}
After this method completes, the rotation/position IS persisted. The next time you call the method, it will transform the object from its current state to wherever your inputs define next.
Also note the compensation for using world coordinates. This allows you to use a point in either world coordinates or local space by converting the object's position vector into the correct coordinate system. It's probably best to use it this way any time your point and object are in different coordinate systems, though your observations may differ.
As a simple solution for anyone trying to quickly change the pivot point of an object, I would recommend creating a group and adding the mesh to the group, and rotating around that.
Full example
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube)
Right now, this will just rotate around its center
cube.rotation.z = Math.PI / 4
Create a new group and add the cube
const group = new THREE.Group();
group.add(cube)
scene.add(group)
At this point we are back where we started. Now move the mesh:
cube.position.set(0.5,0.5,0)
Then move the group
group.position.set(-0.5, -0.5, 0)
Now use your group to rotate the object:
group.rotation.z = Math.PI / 4
I'm working on a project in which I create 3d Widget to rotate, scale and translate a mesh.
I'm currently having some trouble with positioning the cone that I use for scaling the parent mesh.
This is my code:
function createScaleWidget(mesh, constScale){
var bbox = new THREE.Box3().setFromObject(mesh);
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
var coneGeom = new THREE.CylinderGeometry(0, constScale, 10, 50, 20, false);
var cone = new THREE.Mesh(coneGeom,material);
cone.position.y = bbox.max.y/2; //assuming all the objects are laid on the ground
cone.position.z = bbox.max.z+10;
cone.position.x = bbox.max.x+10;
mesh.add(cone);
}
mesh is the mesh I'm creating the widget for,
cone is the widget.
I would like to have the cone in the middle of the mesh (with respect to y)
and in an angle with respect to the bounding box (the ideal would be to have it on the right of the face bounding box that is facing the camera).
Probably my problem is due to the fact that the cone is child of the mesh therefore the final position do not only depends on the bbox but also on the position of the mesh itself.
I guess you want something like that:
You are right you have to locate the cone relatively to its parent position, in this case - the mesh:
function createScaleWidget(mesh, constScale){
var bbox = new THREE.Box3().setFromObject(mesh);
var material = new THREE.MeshBasicMaterial({
color: 0x0000ff
});
var coneGeom = new THREE.CylinderGeometry(0, constScale, 10, 50, 20, false);
var cone = new THREE.Mesh(coneGeom,material);
cone.position.y = bbox.size().y / 2;
mesh.add(cone);
}
this worked for me while i did something similar. the cone should be placed next to one of the faces. to try another face you can then try:
cone.position.z = bbox.size().z / 2;
instead.
I try to draw a point (with a size visible) on a sphere with three.js. This point is at the intersection between the line started from the camera and the sphere.
I was inspired by this link.
you can my result on this link
As you can see, I have hidden the drawPointIntersection() function which is called after render() function. this is in this function that I do instructions to draw this point.
Here's this part of code :
function drawPointIntersection() {
// Direction of camera
var direction = new THREE.Vector3(0, 0, -1);
var startPoint = camera.position.clone();
var ray = new THREE.Raycaster(startPoint, direction);
// Get point of camera direction projected on sphere
var rayIntersects = ray.intersectObject(scene, true);
// Distance between camera and projected point
console.log(rayIntersects[0]);
// Draw point of camera direction on sphere
var dotGeometry = new THREE.Geometry();
// Looking for right syntax with coordinates of intersection point
//dotGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
dotGeometry.vertices.push(new THREE.Vector3(rayIntersects[1]);
//dotGeometry.vertices.push(new THREE.Vector3(rayIntersects.point.x, rayIntersects.point.y, rayIntersects.point.z);
var dotMaterial = new THREE.PointsMaterial({size: 10, sizeAttenuation: false});
var dot = new THREE.Points(dotGeometry, dotMaterial);
scene.add(dot);
}
As you can see, I tried to use different syntax to get the coordinates of the point returned by ray.intersectObject(scene, true) but none works :
dotGeometry.vertices.push(new THREE.Vector3(0, 0, 0));
dotGeometry.vertices.push(new THREE.Vector3(rayIntersects[1]);
dotGeometry.vertices.push(new THREE.Vector3(rayIntersects.point.x, rayIntersects.point.y, rayIntersects.point.z);
I make you notice that camera is rotating around the sphere.
I don't know why it doesn't work, if someone could tell me how to get these coordinates in order to draw the point on the sphere with THREE.Points method with three.js R75.
Thanks in advance
You're in each frame changes the position of the camera, so the wrong direction vector. Try this:
// Camera position
var startPoint = camera.position.clone();
// Direction of camera
var direction = sphere.position.clone().sub(startPoint).normalize();
And just look at the intersection of the target object (sphere):
var rayIntersects = ray.intersectObject(sphere, true);
[ https://jsfiddle.net/3wm9c0jf/ ]
I have a very ordinary rectangle created in Paper.js and I'd like to resize it, but I can't find any obvious ways to do it.
var rect = new Rectangle([0, 0],[width,height]);
rect.center = mousePoint;
var path = new Path.Rectangle(rect, 4);
path.fillColor = fillColor;
path.meta = fillColor;
There's a scale transformation method, but it's not really for mouse interaction and my goal is to create a handle that can resize a component.
Note that PaperJS has three different kinds of Rectangles:
Rectangle — This is the basic type (data structure) that defines a rectangle. Basically, top-left point, width, and height. (Nothing is displayed on the screen.) This kind of rectangle can be resized by setting its size property, for instance:
let rect;
const originalSize = [50, 50];
const newSize = [100, 100];
rect = new Rectangle([10, 50], originalSize);
rect.size = newSize;
Path.Rectangle — This is a method for generating a list of Segments that make up a rectangular-shaped Path. This does get displayed, but a Path lacks methods associated with a rectangle. For instance, a Path.Rectangle has no size property (so trying to modify it has no effect). To resize a Path you can use the scale() method as another answer proposes, or modify its Segments:
rect = new Path.Rectangle([210, 50], originalSize);
rect.strokeColor = "red";
rect.strokeWidth = 3;
rect.segments[0].point = rect.segments[0].point.add([-25, 25]); // lower left point
rect.segments[1].point = rect.segments[1].point.add([-25, -25]); // upper left point
rect.segments[2].point = rect.segments[2].point.add([25, -25]); // upper right point
rect.segments[3].point = rect.segments[3].point.add([25, 25]); // lower right point
Shape.Rectangle — This kind of rectangle gets displayed and exposes properties about its shape, such as size. To resize a Shape.Rectangle you can modify its size property directly:
rect = new Shape.Rectangle([410, 50], originalSize)
rect.strokeColor = "blue"
rect.strokeWidth = 3
rect.size = newSize
Most likely, if you want to draw a rectangle and modify its properties after the fact, the rectangle you are looking for is Shape.Rectangle.
Here is a Sketch that lets you play around with the different kinds of rectangles.
You can calculate the scaling by dividing the intended width/height of your rectangle with the current width/height of your rectangle.
Then you can use that scaling 'coefficient' to apply the scaling.
Based on your code above, you can get the current width/height of your rectangle by using: rect.bounds.width and rect.bounds.height
Here's a function you can use
var rectangle = new Shape.Rectangle({
from: [0, 0],
to: [100, 50],
fillColor: 'red'
});
function resizeDimensions(elem,width,height){
//calc scale coefficients and store current position
var scaleX = width/elem.bounds.width;
var scaleY = height/elem.bounds.height;
var prevPos = new Point(elem.bounds.x,elem.bounds.y);
//apply calc scaling
elem.scale(scaleX,scaleY);
//reposition the elem to previous pos(scaling moves the elem so we reset it's position);
var newPos = prevPos + new Point(elem.bounds.width/2,elem.bounds.height/2);
elem.position = newPos;
}
resizeDimensions(rectangle,300,200)
And here's the Sketch for it.
Be aware that the above function will also reposition the element at it's previous position but it will use top-left positioning. Paper.js uses the element's center to position them so I'm clarifying this so it doesn't cause confusion
I've hit a mental block of sorts, and was looking for some advice or suggestions. My problem is this:
I have a WebGL scene (I'm not using a 3rd party library, except gl-matrix), in which the user can rotate the camera up/down and left/right (rotate around X/Y axis). They can also rotate the model as well (yaw/pitch).
To see the problem, imagine the model has two blocks, A and B in the scene, with A at the center and B to the right (in the viewport), and the rotation center in the center of A. If the user rotates the model, it rotates about the center of block A. But if the user clicks on object B, I need to be able to change the center of rotation to B's center, but still maintain the current camera orientation. Currently, when the center of rotation switches to B, block B moves to the center of the screen, and block A moves to the left. Basically, the code always centers on the current center or rotation.
I use the following code for the modelview matrix update:
var mvMatrix = this.mvMatrix;
mat4.identity(mvMatrix);
mat4.translate(mvMatrix, mvMatrix, this.orbit);
mat4.rotateY(mvMatrix, mvMatrix, this.orbitYaw);
mat4.rotateX(mvMatrix, mvMatrix, this.orbitPitch);
mat4.translate(mvMatrix, mvMatrix, this.eye);
mat4.rotateY(mvMatrix, mvMatrix, this.eyeYaw);
mat4.rotateX(mvMatrix, mvMatrix, this.eyePitch);
I'm trying to figure out what the right yaw and pitch values for orbit and eye I should use in order to move back the current location and to achieve the present camera/eye orientation to avoid the "bounce" from one object to another as the rotation center moves.
I've searched a lot and can't seem to find how best to do this (my current attempt(s) have issues). Any sample code, or just good descriptions would be appreciated.
Edit
I followed gman's advice and tried the following code, but switching orbits just jumped around. My model is composed of multiple objects, and the orbit center can change, but after changing orbits, the orientation of the camera needs to remain steady, which is why I have to calculate the correction to the orbit yaw/pitch and eye yaw/pitch to put the eye back in the same spot and pointing in the same direction after changing orbits. BTW, I only have one orbit yaw and pitch, based on where the current orbit is, so that's a little different from gman's sample:
Camera.prototype.changeOrbit = function (newOrbit) {
var matA = mat4.create();
var matB = mat4.create();
mat4.translate(matA, matA, this.orbit);
mat4.rotateY(matA, matA, this.orbitYaw);
mat4.rotateX(matA, matA, this.orbitPitch);
mat4.translate(matB, matB, newOrbit);
mat4.rotateY(matB, matB, this.orbitYaw);
mat4.rotateX(matB, matB, this.orbitPitch);
var matInverseNewOrbit = mat4.create();
var matNewOrbitToCamera = mat4.create();
mat4.invert(matInverseNewOrbit, matB);
mat4.multiply(matNewOrbitToCamera, matInverseNewOrbit, matA);
var m = matNewOrbitToCamera;
this.eye[0] = m[12];
this.eye[1] = m[13];
this.eye[2] = m[14];
this.eyePitch = ExtractPitch(m);
this.eyeYaw = ExtractYaw(m);
this.update();
};
ExtractPitch and ExtractYaw work as gman had specified, but I do rotate around different axes since pitch is normally defined around the Y axis, and so on. Thanks for the suggestions, though.
I'm not sure I can explain this but basically:
When switching from A to B, at switch time,
Compute the matrix for the camera going around A (the code you have above). (camera)
Compute the matrix for B (matB)
Compute the inverse of the matrix for B. (inverseMatB)
Multiply camera by inverseMatB. (matBtoCamera)
You now have a matrix that goes from B to the camera.
Decompose this matrix (matBToCamera) back into translation and rotation.
Unfortunately I don't know of a good decompose matrix function to point you at. I haven't needed one in a long time. Translation is basically elements 12, 13, 14 of your matrix. (Assuming you are using 16 element matrices which I think is what glMatrix uses).
var translation = [m[12], m[13], m[14]];
For rotation the upper/left 3x3 part of the matrix represents rotation. As long as there is no scaling or skewing involved, according to this page (http://nghiaho.com/?page_id=846) it's
var rotXInRadians = Math.atan2(m[9], m[10]);
var rotYInRadians = Math.atan2(-m[8], Math.sqrt(m[9] * m[9] + m[10] * m[10]));
var rotZInRadians = Math.atan2(m[4], m[0]);
Here's an example
http://jsfiddle.net/greggman/q7Bsy/
I'll paste the code here specific to glMatrix
// first let's make 3 nodes, 'a', 'b', and 'camera
var degToRad = function(v) {
return v * Math.PI / 180;
}
var a = {
name: "a",
translation: [0, -50, -75],
pitch: 0,
yaw: degToRad(30),
};
var b = {
name: "b",
translation: [0, 100, 50],
pitch: 0,
yaw: degToRad(-75),
}
var camera = {
name: "cam",
translation: [0, 15, 10],
pitch: 0,
yaw: degToRad(16),
parent: a,
};
Here's the code that computes the matrix of each
var matA = mat4.create();
mat4.identity(matA);
mat4.translate(matA, matA, a.translation);
mat4.rotateY(matA, matA, a.pitch);
mat4.rotateX(matA, matA, a.yaw);
a.mat = matA;
var matB = mat4.create();
mat4.identity(matB);
mat4.translate(matB, matB, b.translation);
mat4.rotateY(matB, matB, b.pitch);
mat4.rotateX(matB, matB, b.yaw);
b.mat = matB;
var matCamera = mat4.create();
mat4.identity(matCamera);
var parent = camera.parent;
mat4.translate(matCamera, matCamera, parent.translation);
mat4.rotateY(matCamera, matCamera, parent.pitch);
mat4.rotateX(matCamera, matCamera, parent.yaw);
mat4.translate(matCamera, matCamera, camera.translation);
mat4.rotateY(matCamera, matCamera, camera.pitch);
mat4.rotateX(matCamera, matCamera, camera.yaw);
camera.mat = matCamera;
and here's the code that swaps cameras
// Note: Assumes matrices on objects are updated.
var reparentObject = function(obj, newParent) {
var matInverseNewParent = mat4.create();
var matNewParentToObject = mat4.create();
mat4.invert(matInverseNewParent, newParent.mat);
mat4.multiply(matNewParentToObject, matInverseNewParent, obj.mat);
var m = matNewParentToObject;
obj.translation[0] = m[12];
obj.translation[1] = m[13];
obj.translation[2] = m[14];
var rotXInRadians = Math.atan2(m[9], m[10]);
var rotYInRadians = Math.atan2(-m[8], Math.sqrt(m[9] * m[9] + m[10] * m[10]));
var rotZInRadians = Math.atan2(m[4], m[0]);
obj.pitch = rotYInRadians;
obj.yaw = rotXInRadians;
obj.parent = newParent;
};
var newParent = camera.parent == a ? b : a;
reparentObject(camera, newParent);