Threejs - How to offset all points on a 2d geometry by distance - vector

Using Three.js, (although I believe this is more math related) I have a set of 2D points that can create a 2D geometry. such as square, rectangle, pentagon, or custom 2D shape. Based of the original 2D shape, I would like to create a method to offset the points inward or outward uniformly in such a way like the attached image.
I don't know if there is a simple way to offset/grow/shrink all the points (vector3) uniformly on the 2D shape inward or outward. And if so, it'll be cool if I can offset the points by X distance? Kinda of like saying offset the points on the 2D shape outward or inward by X distance.
And no, I'm not referring to scaling from a center point. While scaling may work for symmetrical shapes, it won't work when it comes to non-symmetrical shapes.
see image for example
Thanks in advance.

You can read that forum thread.
I've made some changes with ProfiledContourGeometry and got OffsetContour, so I leave it here, just in case, what if it helps :)
function OffsetContour(offset, contour) {
let result = [];
offset = new THREE.BufferAttribute(new Float32Array([offset, 0, 0]), 3);
console.log("offset", offset);
for (let i = 0; i < contour.length; i++) {
let v1 = new THREE.Vector2().subVectors(contour[i - 1 < 0 ? contour.length - 1 : i - 1], contour[i]);
let v2 = new THREE.Vector2().subVectors(contour[i + 1 == contour.length ? 0 : i + 1], contour[i]);
let angle = v2.angle() - v1.angle();
let halfAngle = angle * 0.5;
let hA = halfAngle;
let tA = v2.angle() + Math.PI * 0.5;
let shift = Math.tan(hA - Math.PI * 0.5);
let shiftMatrix = new THREE.Matrix4().set(
1, 0, 0, 0,
-shift, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
let tempAngle = tA;
let rotationMatrix = new THREE.Matrix4().set(
Math.cos(tempAngle), -Math.sin(tempAngle), 0, 0,
Math.sin(tempAngle), Math.cos(tempAngle), 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
);
let translationMatrix = new THREE.Matrix4().set(
1, 0, 0, contour[i].x,
0, 1, 0, contour[i].y,
0, 0, 1, 0,
0, 0, 0, 1,
);
let cloneOffset = offset.clone();
console.log("cloneOffset", cloneOffset);
shiftMatrix.applyToBufferAttribute(cloneOffset);
rotationMatrix.applyToBufferAttribute(cloneOffset);
translationMatrix.applyToBufferAttribute(cloneOffset);
result.push(new THREE.Vector2(cloneOffset.getX(0), cloneOffset.getY(0)));
}
return result;
}
Feel free to modify it :)

I have some doubts about solutions that do not include number of edges modification.
I faced the same issue in this project where I wanted to ensure a known distance between voronoi cells, and I quickly figured out that scale does not fulfill the use case. But one complication I faced was the disappearance of some edges that I had to handle in a while loop. It was so difficult to debug that I had to create a debug mode that helps see the points and lines, that I also left available. It's possible to activate this debug mode with a checkbox:
Note for the images, I have them as links not embedded as I'm still new contributor (might improve that later).
The edges that shall disappear are shown in red
retraction snapshot1
retraction with edges discard 1
retraction with edges discard 2
Here a link to the function in action, you might have to modify it to have another points format though :
https://github.com/WebSVG/voronoi/blob/8893768e3929ea713a47dba2c4d273b775e0bd82/src/voronoi_diag.js#L278
And here a link to the complete project integrating this function, it has link to a live demo too
https://github.com/WebSVG/voronoi

Related

Find transformation matrix between two matrices

I tried to find the transformation matrix between two matrices, so i can save it, and later apply it on an object so it will maintain its position and rotation relatively to another object. using this sugested solution:
3d (THREE.js) : difference matrix
I used this code:
var aInv1 = new THREE.Matrix4().getInverse(firstObject.matrix.clone());
var aMat2 = new THREE.Matrix4().copy(secondObject.matrix.clone());
var aTrans = new THREE.Matrix4().multiplyMatrices(aMat2, aInv1);
The values of the matrices elements are:
firstObject.matrix.elements = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, -358.483421667927, 1
]
secondObject.matrix.elements = [
0.5137532240102918, -0.844167465362402, 0.15309773101731067, 0,
0.8579380075617532, 0.5055071032079361, -0.091678480502733, 0,
-1.3877787807814457e-17, 0.1784484772418605, 0.983949257314655, 0,
94.64320536824728, 6.92473686011361, -372.0695450875709, 1
]
I would expect that the transformation Matrix aka variable aTrans elements would be 94.64320536824728, 6.92473686011361, 13.58 because those are the differences in the position, but I get 94.64320536824728, 70.89555757320696, -19.340048577797802, 1.
aTrans.matrix.elements = [
0.5137532240102918, -0.844167465362402, 0.15309773101731067, 0,
0.8579380075617532, 0.5055071032079361, -0.091678480502733, 0,
-1.3877787807814457e-17, 0.1784484772418605, 0.983949257314655, 0,
94.64320536824728, 70.89555757320696, -19.340048577797802, 1
]
I would appriciate any educated explanation for this difference, or another way to solve this problem.
You cannot simply add the translations because the second matrix might affect the effective translation of the first.
Let's consider a simple example - suppose both matrices only contain a rotation and translation:
M = R + T
R corresponds to the top-left 3x3 sub-matrix, and T is the first 3 elements of the last column. Multiplication rule with an arbitrary 3D point p:
M * p = R * p + T
Two of these give:
M2 * M1 * p = R2 * R1 * p + R2 * T1 + T2
The last column of M2 * M1 is R2 * T1 + T2 instead of simply T1 + T2, i.e. effective translation that M1 imposes on p is R2 * T1, and not simply T1.

"Almost-structured" 2d quadrilateral mesh in Gmsh

I'd like to generate a two-dimensional quadrilateral mesh using Gmsh. The mesh should be such that its cells are "as quadratic as possible" with a given edge length. That is, if the geometry is simple, I'd expect a perfectly structured grid, and if the geometry is more complex, I'd only expect local distortion.
Consider the following minimal example:
lc = 1;
Point(1) = {0, 0, 0, lc};
Point(2) = {10, 0, 0, lc} ;
Point(3) = {10, 4, 0, lc} ;
Point(4) = {0, 4, 0, lc} ;
Point(5) = {1, 1, 0, lc} ;
Point(6) = {3, 1, 0, lc} ;
Point(7) = {2, 2, 0, lc} ;
Line(1) = {1,2} ;
Line(2) = {3,2} ;
Line(3) = {3,4} ;
Line(4) = {4,1} ;
Line(5) = {5,6} ;
Line(6) = {6,7} ;
Line(7) = {7,5} ;
Line Loop(1) = {4,1,-2,3} ;
Line Loop(2) = {5,6,7} ;
Plane Surface(1) = {1,2} ;
The above yields a highly unstructured mesh, despite there being only a small hole in an otherwise simple and rectangular geometry:
What I'd have in mind is something like this (taken from Automesh2d's web site, a commercial mesh generator):
Can I get a similar "quasi-structured," two-dimensional quadrilateral mesh also using Gmsh (or for that matter, using any open source software)? I'd really appreciate any support.
This should be possible using Transfinite Lines. You define a Transfinite Line between certain points, in your case between the corner points of your large rectangle. While doing so, you can specify how many nodes should be included on this line. If you choose the same number of nodes on the opposing lines of the rectangle, you should be doing fine. Recombine the surface and you're done.
In this simple YouTube-Tutorial, you'll be guided through it step-by-step.
I have quickly tried a similar geometry to yours, this is what I get:
Mesh

Point arrow in vector direction

I'm trying to point an arrow in the direction of a vector (which in this casse is a wind), but am utterly lost.
This is what I've done so far:
PVector wind = new PVector(0.01, 0.00);
float degrees = atan(wind.y / wind.x);
rotate(radians(degrees));
// draw an upwards pointing arrow
I'm no genius with maths; not even sure about if the degrees are right, but it's the only formula I can find on the internet.
I figured something (with help from this thread, https://processing.org/discourse/beta/num_1219607845.html).
noStroke();
fill(0);
rotate(atan2(location.x, location.y));
ellipse(location.x, location.y, 10, 10);
triangle(location.x - -5, location.y + 0, location.x - 5, location.y + 0, location.x - 0, location.y - 10);

How to cut mesh model matching robot template

Given a mesh model (e.g. a box) and a robot template containing volume, aspect ratio and linkage info of sub-parts (basically cuboids), we want to have a cutting algorithm to cut the mesh model into pieces that can match the robot template. We are using Maya for the modelling job.
For example, the mesh model is a 1X1X1 volume=1 box, the robot template has a 1:1:2 volume 0.5 head link with body, and a 1:1:2 volume 0.5 body link with head, then what we need is to cut the box into half.
The matching of volume, aspect ratio and linkage are not strict, reasonable errors can be accepted.
Is there any existing algorithms that can do the job or is there any related topics on this?
Also if you have any idea to solve this problem please enlighten me. Thanks!
EDIT
The problem is, given a mesh object, and a robot template, we need to transform it to the robot.
So now my idea is first cut the object into subparts which match the template, then transform the subparts into robot using Inverse Kinematic maybe.
Sample input and output:
I'd try to just cubes to the correct dimensions by setting the aspect ratio in the cube shape
import maya.cmds as cmds
def scaled_cube(volume, w, d, h):
scale_factor = pow( float(volume) / float(h* w * d), 1.0/3)
return cmds.polyCube(w = w * scale_factor, d = d * scale_factor, h = h * scale_factor)
Edit: After the above comments, this will cut out the portion of a mesh contained in a cuboid (defined here as a maya style bounding box (minx, miny, minz, maxx, maxy, maxz - the same thing you'd get from querying the maya bbox):
def cut_to_fit_bounds(mesh, bbox):
'''
splits an existing mesh
'''
cutmesh = cmds.duplicate(mesh)
minx, miny, minz, maxx, maxy, maxz = bbox
cmds.select(cutmesh)
cmds.polyCut(pc = (minx, 0, 0), ro = (0, 90,0), df =1, ch=0 )
cmds.polyCloseBorder(ch=0)
cmds.polyCut(pc = (maxx, 0, 0), ro = (0, -90,0), df = 1, ch=0)
cmds.polyCloseBorder(ch=0)
cmds.polyCut(pc = (0, 0, minz), ro = (0, 0,0), df =1, ch=0 )
cmds.polyCloseBorder(ch=0)
cmds.polyCut(pc = (0, 0, maxz), ro = (0, 180,0), df = 1, ch=0)
cmds.polyCloseBorder(ch=0)
cmds.polyCut(pc = (0, miny, 0), ro = (-90, 0,0), df =1,ch=0 )
cmds.polyCloseBorder(ch=0)
cmds.polyCut(pc = (0, maxy, 0), ro = (90, 0,0), df = 1, ch=0)
cmds.polyCloseBorder(ch=0)
cmds.select(cutmesh)
The previous routine could be used to create appropriately sized volumes - by placing them correctly and grabbing their bounding boxes with cmds.xform(q=True, bb=True) cut volumes could be made. After that you should have a cut up copy of the original, although I'd worry about normal artifacts, sliver polys and material issues which are common when using polyCut.

Problem with Euler angles from YZX Rotation Matrix

I've gotten stuck getting my euler angles out my rotation matrix.
My conventions are:
Left-handed (x right, z back, y up)
YZX
Left handed angle rotation
My rotation matrix is built up from Euler angles like (from my code):
var xRotationMatrix = $M([
[1, 0, 0, 0],
[0, cx, -sx, 0],
[0, sx, cx, 0],
[0, 0, 0, 1]
]);
var yRotationMatrix = $M([
[ cy, 0, sy, 0],
[ 0, 1, 0, 0],
[-sy, 0, cy, 0],
[ 0, 0, 0, 1]
]);
var zRotationMatrix = $M([
[cz, -sz, 0, 0],
[sz, cz, 0, 0],
[ 0, 0, 1, 0],
[ 0, 0, 0, 1]
]);
Which results in a final rotation matrix as:
R(YZX) = | cy.cz, -cy.sz.cx + sy.sx, cy.sz.sx + sy.cx, 0|
| sz, cz.cx, -cz.sx, 0|
|-sy.cz, sy.sz.cx + cy.sx, -sy.sz.sx + cy.cx, 0|
| 0, 0, 0, 1|
I'm calculating my euler angles back from this matrix using this code:
this.anglesFromMatrix = function(m) {
var y = 0, x = 0, z = 0;
if (m.e(2, 1) > 0.999) {
y = Math.atan2(m.e(1, 3), m.e(3, 3));
z = Math.PI / 2;
x = 0;
} else if (m.e(2, 1) < -0.999) {
y = Math.atan2(m.e(1, 3), m.e(3, 3));
z = -Math.PI / 2;
x = 0;
} else {
y = Math.atan2(-m.e(3, 1), -m.e(1, 1));
x = Math.atan2(-m.e(2, 3), m.e(2, 2));
z = Math.asin(m.e(2, 1));
}
return {theta: this.deg(x), phi: this.deg(y), psi: this.deg(z)};
};
I've done the maths backwards and forwards a few times, but I can't see what's wrong. Any help would hugely appreciated.
Your matrix and euler angles aren't consistent. It looks like you should be using
y = Math.atan2(-m.e(3, 1), m.e(1, 1));
instead of
y = Math.atan2(-m.e(3, 1), -m.e(1, 1));
for the general case (the else branch).
I said "looks like" because -- what language is this? I'm assuming you have the indexing correct for this language. Are you sure about atan2? There is no single convention for atan2. In some programming languages the sine term is the first argument, in others, the cosine term is the first argument.
The last and most important branch of the anglesFromMatrix function has a small sign error but otherwise works correctly. Use
y = Math.atan2(-m.e(3, 1), m.e(1, 1))
since only m.e(3, 1) of m.e(1, 1) = cy.cz and m.e(3, 1) = -sy.cz should be inverted. I haven't checked the other branches for errors.
Beware that since sz = m.e(2, 1) has two solutions, the angles (x, y, z) used to construct the matrix m might not be the same as the angles (rx, ry, rz) returned by anglesFromMatrix(m). Instead we can test that the matrix rm constructed from (rx, ry, rz) does indeed equal m.
I worked on this problem extensively to come up with the correct angles for a given matrix. The problem in the math comes from the inability to determine a precise value for the SIN since -SIN(x) = SIN(-x) and this will affect the other values of the matrix. The solution I came up with comes up with two equally valid solutions out of eight possible solutions. I used a standard Z . Y . X matrix form but it should be adaptable to any matrix. Start by findng the three angles from: X = atan(m32,m33): Y = -asin(m31) : Z = atan(m21,m11) : Then create angles X' = -sign(X)*PI+X : Y'= sign(Y)*PI-Y : Z = -sign(Z)*pi+Z . Using these angles create eight set of angle groups : XYZ : X'YZ : XYZ' : X'YZ' : X'Y'Z' : XY'Z' : X'Y'Z : XY'Z
Use these set to create the eight corresponding matrixes. Then do a sum of the difference between the unknown matrix and each matrix. This is a sum of each element of the unknown minus the same element of the test matrix. After doing this, two of the sums will be zero and those matrixes will represent the solution angles to the original matrix. This works for all possible angle combinations including 0's. As 0's are introduced, more of the eight test matrixes become valid. At 0,0,0 they all become idenity matrixes!
Hope this helps, it worked very well for my application.
Bruce
update
After finding problems with Y = -90 or 90 degrees in the solution above. I came up with this solution that seems to reproduce the matrix at all values!
X = if(or(m31=1,m31=-1),0,atan(m33+1e-24,m32))
Y = -asin(m31)
Z = if(or(m31=1,m31=-1),-atan2(m22,m12),atan2(m11+1e-24,m21))
I went the long way around to find this solution, but it wa very enlightening :o)
Hope this helps!
Bruce

Resources