Three.js update position attribute of mesh from boxGeometry - javascript

I'm implementing an interface for resizing a cube by clicking + dragging its faces.
I'd like to implement this by updating the position attribute on the buffer geometry object, and either recreate the mesh or just set the needsUpdate flag and let it update itself. Neither of these options have worked for me. My latest attempt is below.
this.boxGeo = new THREE.BoxGeometry(2,2,2)
this.boxMesh = new THREE.Mesh(this.boxGeo, this.boxMaterial)
...
// let disp = the amount we want to move the face
// vertex indices
let indices = this.planes[index].indices
// new typed array for position attribute
let positions = new Float32Array(8 * 3)
// for each of the 8 verts
for (let i=0; i < 8; i++) {
if(!indices.includes(i) || disp === 0) {
positions[i * 3] = this.boxGeo.vertices[i].x
positions[i * 3 + 1] = this.boxGeo.vertices[i].y
positions[i * 3 + 2] = this.boxGeo.vertices[i].z
} else {
// modify verts
let d = new THREE.Vector3(disp, disp, disp).multiply(plane.normal)
positions[i * 3] = this.boxGeo.vertices[i].x + d.x
positions[i * 3 + 1] = this.boxGeo.vertices[i].y + d.y
positions[i * 3 + 2] = this.boxGeo.vertices[i].z + d.z
}
}
// update geometry
this.boxMesh.geometry._bufferGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3))
I've tried a few other methods including more closely following the documentation here:
https://threejs.org/docs/#manual/en/introduction/How-to-update-things
Any help or advice would be much appreciated!
EDIT: Per comments below, I'm taking a closer look at my ...attribute.position.array and noticing that it looks like each face lists all its vertices so I can't access them (or set them) as above. Any advice on docs I should go read? Is there an easier way to do this?
So per #Murgen87's comment the code below works to update the position attribute. It looks like the BoxGeometry primitive does not use indexed faces though, and now I'm thinking it might just be easier to scale / translate the box.
let positions =
this.boxMesh.geometry._bufferGeometry.getAttribute('position')
// this loop doesn't pick the right positions for my use case
faces.map((f, i) => {
positions.array[f * 6 + i * 3] += displacement.x
positions.array[f * 6 + i * 3 + 1] += displacement.y
positions.array[f * 6 + i * 3 + 1] += displacement.z
})
positions.needsUpdate = true;
My last remaining question would be why can't I do:
box.geometry.vertices.multiply(displacement)
box.geometry.verticesNeedsUpdate = true
... And this just led me to answer my own question!

The easiest way to do this is:
this.boxMesh.geometry.vertices.map((v,i) => {
if(!planeObj.indices.includes(i)) return
this.boxMesh.geometry.vertices[i].add(displacement)
})
this.boxMesh.geometry.verticesNeedUpdate = true
A valid pattern for updating the position attribute is:
let positions =
this.boxMesh.geometry._bufferGeometry.getAttribute('position')
planeObj.faces.map((f, i) => {
positions.array[f * 6 + i * 3] += displacement.x
positions.array[f * 6 + i * 3 + 1] += displacement.y
positions.array[f * 6 + i * 3 + 1] += displacement.z
})
positions.needsUpdate = true
Note that the loop above does not select the proper elements in the positions.array if just shows how to update if that's what you need to do.
Thanks!

Related

Why isn't this code working to find the sum of multiples of 3 and 5 below the given number?

I've started with some problems on HackerRank, and am stuck with one of the Project Euler problems available there.
The problem statement says: Find the sum of all the multiples of 3 or 5 below N
I've calculated the sum by finding sum of multiple of 3 + sum of multiples of 5 - sum of multiples of 15 below the number n
function something(n) {
n = n-1;
let a = Math.trunc(n / 3);
let b = Math.trunc(n / 5);
let c = Math.trunc(n / 15);
return (3 * a * (a + 1) + 5 * b * (b + 1) - 15 * c * (c + 1)) / 2;
}
console.log(something(1000)); //change 1000 to any number
With the values of num I've tried, it seems to work perfectly, but with two out of five test cases there, it returns a wrong answer (I can't access the test cases).
My question is what is the problem with my code? as the logic seems to be correct to me at least.
Edit: Link to problem page
Some of the numbers in the input are probably larger than what javascript can handle by default. As stated in the discussion on the hackkerrank-site, you will need an extra library (like: bignumber.js) for that.
The following info and code was posted by a user named john_manuel_men1 on the discussion, where several other people had the same or similar problems like yours
This is how I figured it out in javascript. BigNumber.js seems to store the results as strings. Using the .toNumber() method shifted the result for some reason, so I used .toString() instead.
function main() {
var BigNumber = require('bignumber.js');
var t = new BigNumber(readLine()).toNumber();
var n;
for(var a0 = 0; a0 < t; a0++){
n = new BigNumber(readLine());
answer();
}
function answer() {
const a = n.minus(1).dividedBy(3).floor();
const b = n.minus(1).dividedBy(5).floor();
const c = n.minus(1).dividedBy(15).floor();
const sumThree = a.times(3).times(a.plus(1)).dividedBy(2);
const sumFive = b.times(5).times(b.plus(1)).dividedBy(2);
const sumFifteen = c.times(15).times(c.plus(1)).dividedBy(2);
const sumOfAll = sumThree.plus(sumFive).minus(sumFifteen);
console.log(sumOfAll.toString());
}
}

Linear regression with two independent variables in javascript

The following will output the slope, intercept and correlation coefficient R^2 for a given set of x and y values.
let linearRegression = (y,x) => {
let lr = {}
let n = y.length
let sum_x = 0
let sum_y = 0
let sum_xy = 0
let sum_xx = 0
let sum_yy = 0
for (let i = 0; i < y.length; i++) {
sum_x += x[i]
sum_y += y[i]
sum_xy += (x[i]*y[i])
sum_xx += (x[i]*x[i])
sum_yy += (y[i]*y[i])
}
lr['slope'] = (n * sum_xy - sum_x * sum_y) / (n*sum_xx - sum_x * sum_x)
lr['intercept'] = (sum_y - lr.slope * sum_x)/n
lr['r2'] = Math.pow((n*sum_xy - sum_x*sum_y)/Math.sqrt((n*sum_xx-sum_x*sum_x)*(n*sum_yy-sum_y*sum_y)),2)
return lr
}
How can I adapt this to accept two independent variables x1, x2 rather than one?
This page goes into the modified formulas:
http://faculty.cas.usf.edu/mbrannick/regression/Reg2IV.html
But i've been struggling to adapt this to the above function.
Step-by-step
First, look at the input line:
let linearRegression = (y,x) => {. You have 2 variables, so we could call them x1 and x2: let linearRegression = (y,x1,x2) => {
Now the formulae for the regression depend on dot products between the pairs of variables - x1.x1, x1.x2, x1.y etc.
So instead of calculating sum_xx, sum_xy, sum_yy, we need to calculate the sums for all of those pairs (with 3 variables instead of 2, there are 6 sums to calculate).
Finally, the two-variable equation is y=a + b1.x1 + b2.x2, so there are 2 slopes to calculate rather than 1, and the page you linked to gives all the formulae you would need there.

How to calculate coefficients of polynomial expansion in javascript

Suppose I have the following factors:
(1+3x)(1+x)(1+2x)
Expanded to a polynomial, it looks like:
1 + 6x + 11x^2 + 6x^3
The coefficients of this polynomial would be
c0 = 1
c1 = 6
c2 = 11
c3 = 6
I'm trying to figure out how to calculate these rapidly (for any set of factors). The ideal output would be an array of the coefficients, like
var coeff = [c0,c1,c2,c3];
What I'm trying to do is find a way to quickly go from the factors to the array of coefficients. Any suggestions on how to rapidly handle this in javascript? And for the sake of clarity, I'm trying to figure out how to do this for any set of n factors, not just this particular scenario.
You could use the factors as vector and use a cross product for the result.
function multiply(a1, a2) {
var result = [];
a1.forEach(function (a, i) {
a2.forEach(function (b, j) {
result[i + j] = (result[i + j] || 0) + a * b;
});
});
return result;
}
var data = [[1, 3], [1, 1], [1, 2]], // (1+3x)(1+x)(1+2x)
result = data.reduce(multiply);
console.log(result); // [1, 6, 11, 6] = 1x^0 + 6x^1 + 11x^2 + 6x^3
Well I found a method to do what you want from start to finish even without the need for any treatment in the original factors. Although I had to use a Math library. I found there is at least a library that does what you want: Nerdamer
As you can see from the code below the coeficients are correctly calculated from the factors you gave.
var factors = '(1+3x)(1+x)(1+2x)';
console.log('original factors: ' + factors);
var y = nerdamer('expand(' + factors + ')');
var polynomialform = y.toString();
console.log('polynomial form: ' + polynomialform);
var coef = polynomialform.split('+').map(v=>v.trim()).map(v=>v.split('x')[0]).map(v=>v.replace(/^\*+|\*+$/g, ''));
console.log('coeficients: ' + coef);
<script src="http://nerdamer.com/js/nerdamer.core.js"></script>
Notice that coefs var is an array.
Obviously, by the way I otained the coeficients, the operation may fail in different factor cases. This has to be adapted for minus characters and edge cases. You can create some kind of loop and put failed calculations in an array to check for edge cases to adapt the code for the full dataset. I can improve the answer if you provide a larger test dataset.
Hope it helps you.
Here's my take based on the fact that when you multiply (1+ax) by (1+b_1*x+b_2*x^2+...+b_nx^n), in the resulting polynomial (of degree n+1), the first term's coefficient will be one and its last term's coefficient will be a*b_n.
I think it is a bit simpler than the accepted answer, but still quadratic in time. To make this more efficient, you will need more advanced techniques.
function multOne(a, b) {
var n = b.length;
var r = [1]; //The first term is always 1
r[n] = a * b[n - 1]; //The last term is always a*b_n-1
for (var i = 1; i < n; i++)
r[i] = b[i] + a * b[i - 1];
return r;
}
function solve(c) {
var result = [1, c[0]]; //use result as an accumulator
for (var j = 1; j < c.length; j++)
result = multOne(c[j], result);
return result;
}
console.log(solve([3, 1, 2])); //You don't need to pass 1s either. Just pass the varying coefficients
I needed something similar (to calculate permutations via exponential generating functions) so I wrote a version that works when some terms missing, by using objects as the base instead. I also needed it not not calculate anything over a certain power, so that's also an option
/**
* Calculates the result of multiplying many polynomials together
* #example
* polynomials = [{0: 1, 1: 10, 2:1}, {0: 0, 1: 5, 2: 0, 3: 0.5}]
* limit = 4;
* polynomialMultiplication(polynomials, limit);
* #param {Array.<Object.<number,number>>} polynomials an array of polynomials,
* each expressed as an array of term coefficients
* #param {number} limit the maximum term to calculate
* #returns the resultant polynomial from multiplying all polynomials together
*/
function polynomialMultiplication(polynomials, limit) {
const length = limit ?? polynomials.reduce((sum, poly) => sum += Math.max(...Object.keys(poly)), 0)
// make an object to hold the result in
// which is prepopulated by zeros
const template = { ...Array.from({
length
}).fill(0)
};
return polynomials.reduce((memo, poly, polyIndex) => {
const tempCopy = { ...template};
if (polyIndex === 0) return { ...poly };
for (let termIndex = 0; termIndex < length && termIndex <= Math.max(...Object.keys(poly)); termIndex++) {
for (let memoIndex = 0;
(memoIndex + termIndex) <= length && memoIndex <= Math.max(...Object.keys(memo)); memoIndex++) {
const addition = (memo[memoIndex] ?? 0) * (poly[termIndex] ?? 0);
const copyIndex = memoIndex + termIndex;
tempCopy[copyIndex] = (tempCopy[copyIndex] ?? 0) + addition;
}
}
return tempCopy;
}, template)
}
console.log('(1+3x)(1+x)(1+2x)');
const polynomials = [{
0: 1,
1: 3
}, {
0: 1,
1: 1
}, {
0: 1,
1: 2
}];
console.log(polynomialMultiplication(polynomials));
const morePolynomials = [{
0: 1,
1: 0,
2: 5
}, {
0: 0,
1: 6
}, {
0: 1,
1: 2,
2: 0
}];
console.log('(1+5x^2)(6x)(1+2x) up to x^2')
console.log(polynomialMultiplication(morePolynomials, 2));

How is possible this Gaussian Blur javascript algorithm work?

I found the following script to do a blur image processing to an image since javascript. I replicated the sample but I didn't understand how a little part of this script could work fine, I will show the script:
var gaussianBlur = function() {
var data = ctx.getImageData(0,0,canvas.width,canvas.height);
var px = data.data;
var tmpPx = new Uint8ClampedArray(px.length);
tmpPx.set(px);
for (var i = 0, len= px.length; i < len; i++) {
if (i % 4 === 3) {continue;}
px[i] = ( tmpPx[i]
+ (tmpPx[i - 4] || tmpPx[i])
+ (tmpPx[i + 4] || tmpPx[i])
+ (tmpPx[i - 4 * data.width] || tmpPx[i])
+ (tmpPx[i + 4 * data.width] || tmpPx[i])
+ (tmpPx[i - 4 * data.width - 4] || tmpPx[i])
+ (tmpPx[i + 4 * data.width + 4] || tmpPx[i])
+ (tmpPx[i + 4 * data.width - 4] || tmpPx[i])
+ (tmpPx[i - 4 * data.width + 4] || tmpPx[i])
)/9;
};
// data.data = px;
ctx.putImageData(data,0,0);
delete tmpPx;
btnBlur.removeAttribute('disabled');
btnBlur.textContent = 'Blur'; }
This function (gaussianBlur()) is triggered to a html button, so when it's clicked, it will process the image to get it blurred and this process will be repeated according to the number of clicks on the button. The part of the code I don't understand is when it finishes the for loop, all the math operations were saved on the px variable but in the next line code ctx.putImageData(data,0,0);, this data variable doesn't take the new changed variable px. So I wonder, how is it possible that the canvas could render with the image blurred if data variable wasn't changed its data component (because data.data = px as shown in the first lines of gaussianBlur function).
In my opinion, to understand this code, I would put a data.data = px; after the for loop, with this line code I am saying that the new px will be set on data.data variable. And with ot without this line code the algorithm works. So I am confused why it works despite data.data is not set by the new px value. Please help me to understand it.
Thanks in advance
What is happening, is that px is in reality the same object as data.data, meaning that every changes in px take effect in the object you would think is just a copy.
Look at this simple example:
// We create an object 'x' with an array of data
var x = {
data :[ 0, 1]
};
// Assign x.data value to 'p'
var p = x.data;
console.log(p); // [0, 1]
// Then we change one of the array's values to 'Xpto'
p[1] = "Xpto";
// Now look what happened to x.data
console.log(x.data); // [0, "Xpto"]
This is why your code isn't working. px is not a copy, it is a reference.

Weighted average from an array

I have to write the following weighted average formula in JavaScript:
Average = (p1*p2*x1 + p3*p4*x2 + ... +p(n-2)*p(n-1)*xn) / (p1*p2 + p3*p4 + ... + p(n-2)p(n-1) )
The formula gives the average of x values.
I also have an array populated with n elements in JavaScript:
Array = (p1,p2,x1,p3,p4,x2....)
...where pi are the weights and xi the values I want to find the average for.
How can I write the formula using this array?
I would probably use the following strategy:
Create two new arrays (probably weights and values).
Iterate over the original array in steps of 3; multiplying the pn's and pushing the result into weights and pushing the xn into values.
Iterate over the new arrays, creating the weighted total (the left hand of the division) and the total weight (right hand of the division).
Divide one by the other. Done.
In other words, something like this:
function weighted_average(input) {
var weights = [];
var values = [];
var weighted_total = 0;
var total_weight = 0;;
if (input.length % 3 !== 0) {
throw new Error("Input array length is not a multiple of 3.");
}
for (var i = 0; i < input.length; i += 3) {
weights.push(input[i] * input[i + 1]);
values.push(input[i + 2]);
}
for (var i = 0; i < weights.length; i += 1) {
weighted_total += weights[i] * values[i];
total_weight += weights[i];
}
return weighted_total / total_weight;
}
You'll have to verify whether this does exactly what you're after, though. No guarantees. ;)
JSFiddle demo: jsfiddle.net/Z8seZ
Of course, you could skip the intermediary arrays to make it a bit faster. But the above is more explicit and more readable, and therefore more maintainable (e.g. you could easily split out the actual algorithm and create different "wrapper" functions for different forms of input). I would only optimize it if working with (really) large data sets.
Here's a functional approach, requiring ES5:
var w = a.unzip(3).map(function(v, i, a) {
var weight = v[0] * v[1];
var sum = weight * v[2];
return [sum, weight];
}).reduce(function(p, c, i, a) {
return [p[0] + c[0], p[1] + c[1]];
}, [0, 0]);
var aw = w[0] / w[1];
which in pseudo-code is:
split the array into chunks of three
convert each three [p1, p2, x ] into a pair [ p1 * p2 * x , p1 * p2 ]
sum the pairs (along the array, not within each pair)
divide one by the other
and where the (non-standard) unzip function which chunks the array is:
Object.defineProperty(Array.prototype, 'unzip', {
value: function(n) {
n = n || 2;
return this.reduce(function(p, c, i, a) {
if (i % n === 0) {
p.push(a.slice(i, i + n));
}
return p;
}, []);
}
});
ES6 one-liner for an array of objects xs containing the keys w as weight and v as value:
((_w, _v) => _v / _w)(...xs.reduce((r, o) => [r[0] + o[w], r[1] + o[w] * o[v]], [0, 0]))

Categories