Calculate normal vector of a polygon - Newells Method - javascript

I'm trying to calculate the surface normal of a 2D polygon. I am using Newell's method from the OpenGL wiki to calculate the surface normal. https://www.opengl.org/wiki/Calculating_a_Surface_Normal From my understanding the normal should be in the y direction but it always returns [0, 0, 0]. The y value gets changed to -1 on the second iteration and back to zero on the fourth iteration.
p = [[0, 0, 0]
[1, 0, 0]
[0, 0, 1]
[1, 0, 1]]
function calcNormal(p) {
var normal = [0, 0, 0];
for(var i = 0; i < p.length; i++) {
var j = (i + 1) % (p.length);
normal[0] += (p[i][1] - p[j][1]) * (p[i][2] + p[j][2]);
normal[1] += (p[i][2] - p[j][2]) * (p[i][0] + p[j][0]);
normal[2] += (p[i][0] - p[j][0]) * (p[i][1] + p[j][1]);
}
return normal;
}

You're using a degenerate polygon for testing. If you draw it in the xz-plane, with the vertices numbered from 0 to 3, it looks like this:
2 ---- 3
\ /
\ /
\/
/\
/ \
/ \
0 ---- 1
This polygon does not have a well defined normal, since it changes orientation in the middle, and folds over itself.
If you swap the last two vertices:
p = [[0, 0, 0]
[1, 0, 0]
[1, 0, 1]
[0, 0, 1]]
It will look like this, and you should get much more meaningful results:
3 ---- 2
| |
| |
| |
0 ---- 1

OpenGL's version is failing for some cases especially when Polygon is 2D and you providing more than 3 vertices for calculation (4 in your case). If you provide only 3 vertices it will calculate correctly (also consider to use vector product to get normal).
Here is the link to Game Development Stack Exchange to the similar question with different approaches to this problem.

Related

How to compute all possible paths on a grid

I've recently seen a challenge picture on brillant.org's Instagram account:
The instructions:
The robot takes 4 random steps (can't go diagonal).
In which area is it most likely to land?
Obviously there are 44 = 256 possible paths for the robot.
I tried to write a program (Javascript) to solve that problem but my approaches didn't work.
Actually I don't have useful code to show here because I got stuck pretty early on.
So my question:
How would you write a program that:
Checks all 256 possible paths and
Tells me how many (%) landed in which area
This is a very cool question!
And thanks for letting me discover brillant.org's Instagram account.
So, I would proceed as following:
Write a function to calculate all possible permutation with repetition (n^k)
Generate a map where to execute all possible moves calculated in the step 1
Check the area where the robot would land on with the final step and store it
Calculate the percentage based on the counting in step 3
The first step is a problem by itself, and it's not part of this scope. You can use or adapt the code here: https://rosettacode.org/wiki/Permutations_with_repetitions
Then, to generate the map, I simply used an array:
const map = [
0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 1, 1, 2, 1, 1, 0, 0,
0, 1, 1, 2, 2, 2, 1, 1, 0,
1, 1, 3, 3, 2, 3, 3, 1, 1,
0, 1, 1, 3, 3, 3, 1, 1, 0,
0, 0, 1, 1, 3, 1, 1, 0, 0,
0, 0, 0, 1, 1, 1, 0, 0, 0,
0, 0, 0, 0, 1, 0, 0, 0, 0,
];
This is a representation of the image you gave, each area is marked with a different number, that we will reuse later.
At this point I defined an array of the 4 possible moves:
const moves = [
-1, // left
1, // right,
-9, // top
9, // bottom
];
The values indicates the offset needed to move in the direction wrote in in the comment: left and right I guess are self explanatory. For the top and bottom, since we're using an array as "matrix", we basically need to translate the y value to a index value in the array. The formula is simple: index = x + y * width there fore it means if you want to specify a y to move up by one cell you have -1 * 9, and to move down is 1 * 9.
For the same reason the robot's starting position (at the center of the map) is calculate as follow: 4 + 4 * 9.
Now I calculate all the possible moves combination with the permutation function:
const allmoves = permutationsWithRepetition(4, moves);
And create an array to store the results:
let results = [0, 0, 0, 0];
After that, I just iterate all the possible moves array, and calculate the position at the end of the moves:
for (let j = 0; j < allmoves.length; j++) {
// set the robot's initial position at the center
// before executing any moves' list
let pos = 4 + 4 * 9;
// calculate the new position using the current moves
for (let i = 0; i < moves.length; i++) {
let move = allmoves[j][i];
pos += move;
}
// now `map[pos]` points to a number between 1 and 3
// that identify the area where the robot is.
// we use that number as index of the results
// to increment its value.
// Notice that it's impossible land to any 0 area
// if we start from the center and we can only perform
// 4 moves.
// We use that number as `index` for our `results`
results[map[pos]]++;
}
Now in results you will have how many times the robot ended up in which area:
console.log(results); // [0, 60, 100, 96]
As mentioned is impossible given the starting position and the number of moves for the robot to land in any of the 0 area, so the first index would have 0 as value.
You can see that it landed in the area 1 (the orange one) 60 times, in the area 2 100 times (the smallest area, the green / aqua one), and in the area 3, 96 times (the blue / purple one).
At this point you can calculate the percentage (times / total * 100) and display it with a proper formatting:
// we skip the first element of results since
// it's not an area (and we'll be always 0)
for (let k = 1; k < results.length; k++) {
console.log(k, `${(results[k] / allmoves.length * 100).toFixed(2)}%`)
}
And you'll get:
1 "23.44%"
2 "39.06%"
3 "37.50%"
You can also do an empiric check, and actually generate ten thousands of moves randomly and make the program apply those instead of allmoves, and you'll see that you end always with similar number (obviously, but that also the fun part of math, verify that is actually what you will expect!).
Here the a working code that also implement the permutation code mentioned at the beginning, from rosettacode.org, and the code explained in this post: https://codepen.io/zer0/pen/RwWPZmE
(You need to open the console to see the results)
I would create different objects representing the different possibilities like below:
function Path(x, y, movesLeft) {
this.x = x;
this.y = y;
this.movesLeft = movesLeft;
this.paths = [];
if (movesLeft > 0) {
this.paths.push(new Path(x - 1, y, movesLeft - 1));
this.paths.push(new Path(x + 1, y, movesLeft - 1));
this.paths.push(new Path(x, y - 1, movesLeft - 1));
this.paths.push(new Path(x, y + 1, movesLeft - 1));
}
this.getArray = function() {
if (this.movesLeft > 0) {
var output = [];
for (var i = 0; i < this.paths.length; i++) {
output = output.concat(this.paths[i].getArray());
}
return output;
}
return [this];
}
}
Now, you can create an object and test the results:
var endPosArray = new Path(0, 0, 4).getArray();
All you need to do is loop through the array and calculate the chances.

Group rectangles in a grid

I have a randomly-sliced rectangular grid - width is 80 unit.
I already have the free spaces of each row of my grid stored in an array like this below:
[
{pX:1,sX:15},
{pX:30,sX:13},
{pX:43,sX:1},
{pX:44,sX:17}
],
[
{pX:1,sX:15},
{pX:16,sX:14},
{pX:30,sX:13},
{pX:43,sX:1},
{pX:44,sX:17}
]
where pX is the starting point and sX represent the width of each rectangle.
Some of the array entries are adjacent, i.e. pX[i]+sX[i] = pX[i+1]. How can i group these array entries together and get the resulting rectangles with the maximum adjacent width?
You need to tighten arrays, joining adjacent segments. This code (Delphi, consider it as pseudocode) shrinks arrays in needed manner:
var
pX, sX: TArray<Integer>;
i, removed: Integer;
begin
pX := [1, 30, 43, 44, 64, 66, 69, 72];
sX := [15, 13, 1, 17, 2, 2, 3, 5];
removed := 0;
for i := 1 to High(pX) do begin
if (pX[i - removed - 1] + sX[i - removed - 1] = pX[i]) then
begin ////join neighbors
sX[i - removed - 1] := sX[i - removed - 1] + sX[i];
Inc(removed); ////removed++
end
else
if (removed > 0) then
begin ////copy to new place
pX[i - removed] := pX[i];
sX[i - removed] := sX[i];
end;
end;
////shorten array, remove tail
SetLength(px, Length(pX) - removed);
SetLength(sX, Length(sX) - removed);
////output result
Memo1.Lines.Add(ArrayToString(pX));
Memo1.Lines.Add(ArrayToString(sX));
output
1 30 64 69
15 31 4 8

How can I add faces to an indexed THREE.BufferGeometry?

Say that I had generated a THREE.BufferGeometry from a THREE.Geometry named oldGeom like so:
// using WebGLRenderer
var geometry = new THREE.BufferGeometry();
var indices = new Uint16Array(oldGeom.vertices.length);
var vertices = new Float32Array(oldGeom.vertices.length * 3);
for (var i = 0; i < oldGeom.vertices.length; i++) {
indices[i] = i;
vertices[i * 3 + 0] = oldGeom.vertices[i].x;
vertices[i * 3 + 1] = oldGeom.vertices[i].y;
vertices[i * 3 + 2] = oldGeom.vertices[i].z;
}
geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3));
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
Hopefully I have the indexing right. At this point, how could I add a face using the indices? I'm planning to loop through the faces of oldGeom to add them all here, but I can't find any documentation on this. Thanks!
Similar to this question, but with an indexed geometry.
From the documentation for BufferGeometry:
index (itemSize: 3)
Allows for vertices to be re-used across multiple triangles; this is called using "indexed triangles," and works much the same as it does in Geometry: each triangle is associated with the index of three vertices. This attribute therefore stores the index of each vertex for each triangular face. If this attribute is not set, the renderer assumes that each three contiguous positions represent a single triangle.
The way "indexed triangles" work is that "position" is an array of numbers, with every consecutive set of 3 numbers representing one vertex (x, y, z). "Index" is an array of numbers, where every consecutive set of 3 numbers represents one face, by referring to the indices of vertices in the "position" array.
You might have an array of vertices like this:
var vertices = [0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0];
You can think of this array as sets of XYZ coordinates like this:
var vertices = [
0, 0, 0, // vertex index 0
1, 0, 0, // vertex index 1
1, 1, 0, // vertex index 2
0, 1, 0 // vertex index 3
];
Now if you have an index array like this:
var indices = [0, 1, 2, 1, 2, 3];
It represents two triangles:
var indices = [
0, 1, 2, // face with vertices at indices 0, 1, 2
1, 2, 3 // face with vertices at indices 1, 2, 3
];
So triangle #1 has vertices at XYZ (0, 0, 0), (1, 0, 0), (1, 1, 0) while triangle #2 has vertices at XYZ (1, 0, 0), (1, 1, 0), (0, 1, 0).
On the other hand you can define vertices without using an index. The power of indexing is that it lets you reuse vertices defined in the array instead of listing them redundantly every time they appear in a triangle. If you have a single array, vertices, then quite simply, every set of 9 numbers in the array is one triangle (three sets of consecutive vertices, each with three consecutive XYZ values).
Going back to your original question, if you want to add triangles to your BufferedGeometry, I see two basic options:
Add the triangles to the original oldGeom object, and then convert it. It's a lot easier to add triangles to Geometry than it is BufferGeometry. Remember that the whole point of BufferGeometry is that it's not supposed to change! You would also be able to take advantage of .fromGeometry() because the new faces are already defined in oldGeom.
Make an indices array that's larger than necessary for the original indices and manually define triangles there. If you're defining new vertices that don't exist in the vertices array then you'd have to add them in there too. What a pain in the butt.

How to draw walls in ThreeJS from path or 2d array?

I need to draw a 3d house model (walls only) from a 2d path or array (explained later) I receive from FabricJS editor I've built. The type of data sent from 2d to 3d views doesn't matter.
My first (and only quite close to what I want to get) attempt was to create the array of 1s and zeros based on the room I want to draw, and then render it in ThreeJS as one cuboid per 'grid'. I based this approach on this ThreeJS game demo. So if the array look like this:
var map = [ //1 2 3 4 5 6 7 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
[1, 1, 0, 0, 0, 0, 0, 1, 1, 1,], // 1
[1, 1, 0, 0, 1, 0, 0, 0, 0, 1,], // 2
[1, 0, 0, 0, 1, 1, 0, 0, 0, 1,], // 3
[1, 0, 0, 1, 1, 1, 1, 0, 0, 1,], // 4
[1, 0, 0, 0, 1, 1, 0, 0, 1, 1,], // 5
[1, 1, 1, 0, 0, 0, 0, 1, 1, 1,], // 6
[1, 1, 1, 0, 0, 1, 0, 0, 1, 1,], // 7
[1, 1, 1, 1, 1, 1, 0, 0, 1, 1,], // 8
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1,],
];
I iterate through the array and render one block for every 1, and calculate it's position from indexes from the 2d 'map' (my array).
var UNITSIZE = 250, units = mapW;
for (var i = 0; i < mapW; i++) {
for (var j = 0, m = map[i].length; j < m; j++) {
if (map[i][j]) {
var wall = new t.Mesh(cube, material);
wall.position.x = (i - units/2) * UNITSIZE;
wall.position.y = WALLHEIGHT/2;
wall.position.z = (j - units/2) * UNITSIZE;
scene.add(wall);
}
}
}
It worked great till I wanted to place other models (.obj, but it doesn't matter. Let's call them furniture) near the walls. Each piece of furniture has it's (x=0, y=0, z=0) point in the center of the model, and since walls are cubes (with the same coord system, with 0 point in the center), furniture are rendered in the center of the wall (when we place it in the corner, only 1/4 of the model is visible). This is more/less how it looks like:
(black - how the walls should look like, blue - each cuboid of the wall, red - piece of furniture)
Thats why I would like to render walls as planes, probably from a 2d closed patch (I can export it from Fabric without a problem). I don't need walls to be thick nor to be visible "from behind", when camera moves through the wall. Any clues on how to achieve something like this?
"Help me StackOverflow, your my only hope."
You can manually populate the vertex and face arrays of a THREE.js mesh, so if you can export the closed path you need for example as an array of coordinates, you can iterate over it, and push needed information to your wall object.
Something like this
var coordArray = [...]; //Array of corner points of a closed shape from your source. Here assumed to be THREE.Vector2() for simplicity.
var walls = new THREE.Geometry();
for(var i = 0; i < coordArray.length(); i++){ //iterate over the coordinate array, pushing vertices to the geometry
var coordinates = coordArray[i];
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 0)); //vertex at floor level
walls.vertices.push(new THREE.Vector3(coordinates.x, coordinates.y, 10)); //vertex at the top part of the wall, directly above the last
}
var previousVertexIndex = walls.vertices.length - 2; // index of the vertex at the bottom of the wall, in the segment we are creating faces for
for(var i = 0; i < walls.vertices.length; i += 2){
walls.faces.push(new THREE.Face3(i, i + 1, previousVertexIndex));
walls.faces.push(new THREE.Face3(i + 1, previousVertexIndex + 1, previousVertexIndex));
previousVertexIndex = i;
}
walls.computeVertexNormals();
walls.computeFaceNormals();
scene.add(new THREE.Mesh(walls, new THREE.MeshLambertMaterial());

Solve max height of up to 20 objects

I have 1 to 20 objects inside a canvas.
The objects have left, width, top and height attributes.
I divided my canvas into 12 sections. Each objects width can be 1/12th of the canvas width (month) or 3/12th (quarter).
Now I need to get the sum of the heights of all objects inside each of the 12 grid sections.
For example.
In section 1 is 1 month-object with the height 12 --> result 12
In section 2 are 2 month-objects. heights 10 and 14 --> result 24
In section 4 are 1 month-object and one quarter object: heights 12 and 14 -->result 26
In section 5 are 1 month-object and the same quarter object: heights 4 and 14 --> result 18
Hope this was understandable. Solution is needed in javascript.
What I have is an array with all the beginning sections and all the ending sections.
For example:
startingSections = [1, 2, 2, 4, 4, 5]
endingSections = [1, 2, 2, 4, 6, 5]
My desired output is the following:
var sectionsMax = [];
sectiosnMax = getMonthlyTops();
function getMonthlyTops(){
var monthlyTops = [];
/* Code here */
return monthlyTops;
}
Results could be:
sectionsMax = [12, 24, 0, 26, 18, 14, 0, 0, 0, 0, 0, 0]
Example picture:
Can you try this and post what you get please. Since you can loop through the objects, i dont think you actually need startingSections and endingSections arrays.
var monthlyTops = new Array(0,0,0,0,0,0,0,0,0,0,0,0);
canvas.forEachObject(function (targ) {
x = targ.left / (canvas.width / 12);
for (i = x; i < (x + targ.width / (canvas.width / 12) ); i++)
monthlyTops[i] += targ.height;
});
return monthlyTops;

Categories

Resources