threejs applyMatrix4 appears to do nothing - three.js

The method applyMatrix4 seems like it does nothing...
Why can I not apply this transformation matrix to my vector?
const vec = new THREE.Vector3(1,1,1)
const geometry = new THREE.BoxGeometry(1,1,1)
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 })
const mesh = new THREE.Mesh(geometry, material)
mesh.rotateX(Math.PI)
const rotatedVec = vec.applyMatrix4(mesh.matrix)
console.log(rotatedVec)
Expectation (taking the cross product):
{x: 1, y: -1, z: -1}
Reality (the vector is unchanged)
{x: 1, y: 1, z: 1}
My mesh's matrix has changed - it is not the identity matrix.
[
[1, 0, 0, 0],
[0, -1, 0, 0],
[0, 0, -1, 0],
[0, 0, 0, 1],
]

Object3D.rotateX() only affects the object's quaternion property. It does not update its local matrix. If you say your matrix has changed, I assume you have checked it at a later point when other engine logic triggers a recalculation.
You can solve this issue by adding mesh.updateMatrix(); after you have called Object3D.rotateX().
Or even better use Vector3.applyQuaternion(). In this way, you don't have to recompute the matrix because you don't need it anyway.
const rotatedVec = vec.applyQuaternion(mesh.quaternion)

Related

Find the Z coordinate of point within plane

From the 3 black points I found the plane
const { Vector3, Plane } = require('three')
const points = [new Vector3(0, 0, 0), new Vector3(1, 0, 1), new Vector3(1, 2, 0)]
const plane = new Plane().setFromCoplanarPoints(...points)
But how do I get the Z coordinate of the fourth red point (example: (0.75, 0.75, z)) that lies in the plane?
This doesn't seem to work:
const targetPoint = new Vector3()
plane.projectPoint(new Vector3(0.75, 0.75, 0), targetPoint)
/*
Vector3 {
x: 0.5833333333333334,
y: 0.8333333333333334,
z: 0.16666666666666666
}
*/
An answer with TurfJS would be also perfectly OK
Just for others to know, I solved with TurfJS using its method planepoint.
The method polygon has as 1st parameter an array of Linear Rings, and a Linear Ring must have the same first and last points, thus a triangle has 4 points. a, b, c represent the ordered heights.
const turf = require('#turf/turf')
const point = turf.point([0.75, 0.75])
const triangle = turf.polygon([[
[0, 0], [1, 0], [1, 2], [0, 0]
]], {
a: 0,
b: 1,
c: 0
})
const zValue = turf.planepoint(point, triangle) // 0.375

Tensorflow.js: Resize image to specific byte size

For the prediction I need an image of the shape [null,7,7,256].
const image = tf.reshape(tf.fromPixels(loadedImage).resizeBilinear([?,?]), [null, 7, 7, 256]);
But I don't know how to resize the image to be exactly 7*7*256 big.
Error: Size(37632) must match the product of shape ,7,7,256
Edit: The code for the prediction is:
tf.loadModel(tf.io.browserFiles([uploadJSONInput.files[0], uploadWeightsInput.files[0]])).then(model => {
console.log("model loaded");
return model;
}).then(pretrainedModel => {
return loadImage2('http://localhost/myimg.jpeg', (src) => {
const loadedImage = document.createElement("img");
loadedImage.src = src;
loadedImage.width = "275"
loadedImage.height = "183"
console.log("image loaded");
const image = tf.fromPixels(loadedImage)
resized = tf.image.resizeBilinear(image, [7, 7])
const padded = resized.pad([[0, 0], [0, 0], [126, 127]])
const pretrainedModelPrediction = pretrainedModel.predict(padded);
const modelPrediction = model.predict(pretrainedModelPrediction);
const prediction = modelPrediction.as1D().argMax().dataSync()[0];
console.log(prediction);
});
})
Error:
Error: Error when checking : expected flatten_Flatten1_input to have 4 dimension(s), but got array with shape [7,7,256]
ResizeBilinear will resize the height and the width of the image, meaning that it does not affect the number of channel which is the last dimension of the shape of an image.
If your image has 256 as it last channel, then the following will work
tf.fromPixels(loadedImage).resizeBilinear([7,7])
Reshaping a tensor will only work if both sizes matches.
const image = tf.ones([183, 275, 3 ])
resized = tf.image.resizeBilinear(image, [7, 7])
console.log(resized.pad([[0, 0], [0, 0], [126, 127]]).shape);
An image is generally of shape [h, w, 3].
resize = tf.fromPixels(loadedImage).resizeBilinear([7,7]) // [7, 7, 3]
And then use tf.pad for the last dimension
const image = tf.ones([183, 275, 3 ])
resized = tf.image.resizeBilinear(image, [7, 7])
console.log(resized.pad([[0, 0], [0, 0], [126, 127]]).shape);// [7,7,256]
// reshape the tensor to be a 4d
resized.reshape([1,7,7,256])
Here's how to do it with a Uint8Array
const canvas: any = document.getElementById('canvas')
const context = canvas.getContext('2d')
const imageData: ImageData = context.getImageData(0, 0, canvas.width, canvas.height)
const uint8array = new Uint8Array(imageData.data.buffer)
const rgbaTens3d = tf.tensor3d(uint8array, [canvas.height, canvas.width, 4])
const rgbTens3d= tf.slice3d(rgbaTens3d, [0, 0, 0], [-1, -1, 3]) // strip alpha channel
const smallImg = tf.image.resizeBilinear(rgbTens3d, [192, 192]); // 192,192 is dictated by my model

three.js set objects in local coordinate system of another object

I was wondering if it is possible to place 3d objects within a local coordinate system of another object in three.js. so the placed objects should be relative to the "origin" object.
for example: i scan an image with the device camera (with expo AR) and want to place objects which got a fixed distance to the image. I think these positions are relative to the camera, isn't it?
this.scene.add(chair)
chair.position.set( 1, 0, -5);
this.scene.add(chair2)
chair2.position.set( 1, 0 , -3);
I think what you're looking for is the THREE.Group object. It lets you nest elements in a group, so if you change the coordinates of the group, its children move with it. For instance:
// Create parent, set its pos to 0, 5, 0
var parent = new THREE.Group();
scene.add(parent);
parent.position.set(0, 5, 0);
// create child, and add to parent
var chair1 = new THREE.Mesh(geom, mat);
parent.add(chair1);
chair1.position.set(1, 0, 0);
// create child, and add to parent
var chair2 = new THREE.Mesh(geom, mat);
parent.add(chair2);
chair2.position.set(0, 1, 0);
chair1 will be at [1, 5, 0] in global coordinates, but [1, 0, 0] in local space.
chair2 will be at [0, 6, 0] in global coordinates, but [0, 1, 0] in local space.

Animate on bézier with ScrollMagic: Initial state = first bézier point

See a jsfiddle here
I am tweening along a bézier path with 3 points.
// bezier data
var bezierData = {
curviness: 1,
autoRotate: false,
values: [
{x: 0, y: 0, rotation:"40_cw"}, /* <-- The desired state of the object before any animation has happened */
{x: 20, y: 0, rotation:"0_ccw"},
{x: 40, y:0, rotation:"-20_ccw"}
]
};
// build tween
var tween = new TimelineMax()
.add(TweenMax.to("#testobj", 1, {css:{bezier: bezierData}, ease:Power1.easeInOut}));
// create scene
var scene = new ScrollMagic.Scene({
triggerElement: "#testobj",
duration: 100,
offset: 10
})
.setTween(tween)
.addTo(ctrl)
.addIndicators();
What I want: The initial state of my object (i.e. before any animation has happened) should be the first bézier point, {x: 0, y: 0, rotation:"40_cw"}.
What's happening: The initial state is the object's default style, i.e. the equivalent of {x: 0, y: 0, rotation:"0"}. Note how in the jsfiddle the green square starts out upright while I want it to start rotated 40° clock-wise.
Tahir Ahmed's answer works!
perhaps you can use .set() before doing the .to() tween? something like this.

How do you connect a geometry to two moving vertices

I have created some box geometries in my threejs app and I have successfully drawn a cylinder from the center of one to the center of another using the code below:
function cylinderMesh(pointX, pointY, material) {
var direction = new THREE.Vector3().subVectors(pointY, pointX);
var orientation = new THREE.Matrix4();
orientation.lookAt(pointX, pointY, new THREE.Object3D().up);
orientation.multiply(new THREE.Matrix4(1, 0, 0, 0,
0, 0, 1, 0,
0, -1, 0, 0,
0, 0, 0, 1));
var edgeGeometry = new THREE.CylinderGeometry(2, 2, direction.length(), 8, 1);
var edge = new THREE.Mesh(edgeGeometry, material);
edge.applyMatrix(orientation);
edge.position.x = (pointY.x + pointX.x) / 2;
edge.position.y = (pointY.y + pointX.y) / 2;
edge.position.z = (pointY.z + pointX.z) / 2;
return edge;
}
scene.add(cylinderMesh(vertex1, vertex2, globalMaterial));
My question is: How to I keep the cylinder "connected" to the two vertices I provide if they move?
I don't want to use a THREE.Line because I can't control the width of the line and I have noticed weird issues with clipping if the camera gets too close.
Any ideas?

Resources