I notice that multmatrix() takes matrices in the traditional format where, for example, translation terms are the in the last column. However, matrix maths in OpenSCAD user land is done with transposed matrices, where translation terms are on the last row. I understand this is because vectors are treated as rows instead of columns when doing the multiply. It is inconvenient when at the end of calculating some transformation I want to pass the result to multmatrix and have to transpose it. Experimentally it seems that if I reverse the order of multiplication, so that instead of row vector times transformation matrix I have matrix times row vector then I can use traditional format transformation matrices and pass them directly to multmatrix. Is this correct? It seems too simple. m = translate([1,2,3]); m2 = transpose(m); echo(m); echo(m2); echo([4,5,6, 1] * m); echo(m2 * [4,5,6,1]); ECHO: [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 2, 3, 1]] ECHO: [[1, 0, 0, 1], [0, 1, 0, 2], [0, 0, 1, 3], [0, 0, 0, 1]] ECHO: [5, 7, 9, 1] ECHO: [5, 7, 9, 1] If I just rewrite all my transformations in conventional order and swap just the multiplication order with row vectors, will everything work out? _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
This statement seems to imply the answer is yes:
https://en.wikipedia.org/wiki/Matrix_multiplication#Transpose On Mon, 1 Apr 2019 at 14:28, nop head <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Mathematically, the product of a matrix by a row matrix is undefined (except if the matrix has just one column) but in OpenSCAD, the vector is considered a column vector instead of a row vector in the product of a matrix by a vector. On the other hand, the product of a vector by a matrix in OpenSCAD has the same meaning it has in Mathematics. So, in Mathematics, the following holds for a row vector v and matrix M : v * M = transpose(M)*transpose(v) but in OpenSCAD we get an undefined vector in the evaluation of the right expression. Em seg, 1 de abr de 2019 às 14:33, nop head <[hidden email]> escreveu:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Yes OpenSCAD seems unable to handle single column vectors but the transpose of a column vector is a row vector, so I think that is why it works when I swap the order. The order has been swapped and both operands have been transposed, so the result is the same but now the matrix is in conventional format. So now I have conventional transformation matrices that can be passed directly to multmatrix simply by swapping the multiplication order. The other thing that changed is the order that I have to multiply transformation matrices with each other is reversed. This seems more natural because translate () rotate() in modules becomes translate() * rotate() in functions, whereas before I had do it in reverse, which seemed wrong. I used it to speed up the preview of this box from 63 seconds to 5 seconds. All the time was taken making the holes in the sheets. In 3D it is slow because of CGAL hates lots of holes. When I made the sheets 2D I had to project the holes and that was not much faster as projection() is very slow. Now I represent the holes positions and orientation with matrices and project the holes with matrix multiplication and filter the ones that lie on the sheet like this. module drill_holes(type, t) for(list = [corner_holes(type), side_holes(type)], p = list) let(q = t * p) if(abs(transform([0, 0, 0], q).z) < eps) multmatrix(q) drill(screw_clearance_radius(bbox_screw(type)), 0); t is the transformation that maps the holes for each sheet to the X plane. On Mon, 1 Apr 2019 at 16:15, Ronaldo Persiano <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by Ronaldo
I experimented a bit with these operations and then I also read the manual.
:) Matrix*matrix products work exactly as expected, when both matrices are lists of lists. What is perhaps a bit trickier is the handling of vectors, where a vector is a list of scalars. Note that there is no meaningful notion of "row vector" vs "column vector" for a list of scalars, nor is there any meaningful notion of transpose---these objects are just vectors. So I can't make sense of "transpose(v)" as an OpenSCAD operation on a vector, v. How would it change? Simply put, when you do matrix*vector or vector*matrix operations, OpenSCAD treats the vector as either a column or row as needed to make the operation possible. So in OpenSCAD, M*v = v*transpose(M). But if you want to transform a list of points, then you have to pay attention to the orientation of the data, so M*[v1,v2,v3,v4,...] will fail because you have now organized the data as a matrix of row vectors, whereas multiplying on the right (by the transpose) will work: [v1,v2,v3,v4,...]*transpose(M). And of course it would also work to do M*transpose([v1,v2,v3,v4,...]) but then the result would need to be transposed again to get a list of points. Ronaldo wrote > Mathematically, the product of a matrix by a row matrix is undefined > (except if the matrix has just one column) but in OpenSCAD, the vector is > considered a column vector instead of a row vector in the product of a > matrix by a vector. On the other hand, the product of a vector by a matrix > in OpenSCAD has the same meaning it has in Mathematics. So, in > Mathematics, > the following holds for a row vector v and matrix M : > > v * M = transpose(M)*transpose(v) > > but in OpenSCAD we get an undefined vector in the evaluation of the right > expression. -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Simply put, when you do matrix*vector or vector*matrix operations, OpenSCAD treats the vector as either a column or row as needed to make the operation possible. No it doesn't. In conventional maths vector * transformation matrix the vector is a column. When you do it in OpenSCAD the result you get is what it would be for a column vector if the matrix was transposed. That makes the transformation matrix wrong for multmatrix, which expects it in conventional format as you would see in Wikipedia, for example. All the user land matrix maths I have seen for OpenSCAD works with transposed transformations. I found that simply by post multiplying by the vector you get the correct answer without having to transpose the matrix. So I have rewritten my maths utilities with conventional transformation matrices. function sqr(x) = x * x; function translate(v) = let(u = is_list(v) ? len(v) == 2 ? [v.x, v.y, 0] : v : [0, 0, v]) [ [1, 0, 0, u.x], [0, 1, 0, u.y], [0, 0, 1, u.z], [0, 0, 0, 1] ]; function rotate(a, v) = is_undef(v) ? let(av = is_list(a) ? a : [0, 0, a], cx = cos(av[0]), cy = cos(av[1]), cz = cos(av[2]), sx = sin(av[0]), sy = sin(av[1]), sz = sin(av[2])) [ [ cy * cz, cz * sx * sy - cx * sz, cx * cz * sy + sx * sz, 0], [ cy * sz, cx * cz + sx * sy * sz,-cz * sx + cx * sy * sz, 0], [-sy, cy * sx, cx * cy, 0], [ 0, 0, 0, 1] ] : let(s = sin(a), c = cos(a), C = 1 - c, m = sqr(v.x) + sqr(v.y) + sqr(v.z), // m used instead of norm to avoid irrational roots as much as possible u = v / sqrt(m)) [ [ C * v.x * v.x / m + c, C * v.x * v.y / m - u.z * s, C * v.x * v.z / m + u.y * s, 0], [ C * v.y * v.x / m + u.z * s, C * v.y * v.y / m + c, C * v.y * v.z / m - u.x * s, 0], [ C * v.z * v.x / m - u.y * s, C * v.z * v.y / m + u.x * s, C * v.z * v.z / m + c, 0], [ 0, 0, 0, 1] ]; function scale(v) = let(s = len(v) ? v : [v, v, v]) [ [s.x, 0, 0, 0], [0, s.y, 0, 0], [0, 0, s.z, 0], [0, 0, 0, 1] ]; function vec3(v) = [v.x, v.y, v.z]; function transform(v, m) = vec3(m * [v.x, v.y, v.z, 1]); function transform_points(path, m) = [for(p = path) transform(p, m)]; This makes far more sense to me as I can pass my matrices to multmatrix without transposing them. Note that in my transform(v, m) I post multiply by v whereas if OpenSCAD treated v as a column it should be pre-multiplied. I think if you transpose a row vector you would get a column vector. On Tue, 2 Apr 2019 at 18:25, adrianv <[hidden email]> wrote: I experimented a bit with these operations and then I also read the manual. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
> In conventional maths vector * transformation matrix the vector is a column. This is not a convention I am familiar with. All the references I have consulted say that the vector is a row, and that's the convention I use in my own code. ------ Here's what I use as a reference for "conventional maths": http://mathworld.wolfram.com/MatrixMultiplication.html quote: "(a×b) denotes a matrix with a rows and b columns". In OpenSCAD, an a×b matrix is written in row-major order. Here's a 2×3 matrix: [ [11,12,13], [21,22,23] ] In matrix multiplication, "the dimensions of the matrices must satisfy (n×m)(m×p) = (n×p)". For example, (1×3)(3×3) = (1×3). Therefore, in the maths convention that I know, as defined by MathWorld, vector*matrix, the vector is a row vector. OpenSCAD conforms to this convention. And for what it's worth, the OpenSCAD matrix multiply is consistent with the behaviour of the Mathematica matrix multiply (both use nested lists to represent matrices). The wikipedia article Matrix Multiplication defines the same conventions. Also, https://en.wikipedia.org/wiki/Row_and_column_vectors says that in 'vector*matrix', the vector is a row vector. However, in conventional maths, if you write matrix*vector, then in that case, the vector is a column vector. Also, when you transform a vector using a transformation matrix, the convention is to perform the multiplication in this order: transformation_matrix * vector This convention is mentioned in both Mathworld and in the Wikipedia article "Transformation_matrix". Doug Moen. On Tue, Apr 2, 2019, at 3:40 PM, nop head wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
OK so OpenSCAD is correct and we need to post multiply with the vector to have it treated as a column. Somehow I had it all transposed. I was sure it came from looking at other people's code but perhaps it was just me getting confused. On Tue, 2 Apr 2019 at 22:30, Doug Moen <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by nophead
nophead wrote
>> >> Simply put, when you do matrix*vector or vector*matrix operations, >> OpenSCAD treats the vector as either a column or row as needed to make >> the >> operation possible. > > > No it doesn't. In conventional maths vector * transformation matrix the > vector is a column. This is not true. There are two conventions. In my experience it's more common to choose the convention of column vectors with transformation operators represented by matrix multiplication on the left. But it is entirely valid to choose the convention of row vectors and transformations as matrices multiplying on the right, and some authors definitely make this choice. > When you do it in OpenSCAD the result you get is what > it would be for a column vector if the matrix was transposed. That makes > the transformation matrix wrong for multmatrix, which expects it in > conventional format as you would see in Wikipedia, for example. > > All the user land matrix maths I have seen for OpenSCAD works with > transposed transformations. I found that simply by post multiplying by the > vector you get the correct answer without having to transpose the matrix. > So I have rewritten my maths utilities with conventional transformation > matrices. I have not studied a lot of OpenSCAD code, so I don't know what people tend to do. I'm just observing what OpenSCAD itself does. > function transform(v, m) = vec3(m * [v.x, v.y, v.z, 1]); > function transform_points(path, m) = [for(p = path) transform(p, m)]; > > This makes far more sense to me as I can pass my matrices to multmatrix > without transposing them. Note that in my transform(v, m) I post multiply > by v whereas if OpenSCAD treated v as a column it should be > pre-multiplied. > > I think if you transpose a row vector you would get a column vector. In your transform(v,m) you have the matrix on the left and the vector on the right, which means the vector is a column vector. Can you transpose a row vector and get a column vector? Please show me. It appears that I was not clear in my explanation of how OpenSCAD works. At the risk of repeating myself here, it is necessary to distinguish between operations that include a vector, meaning a list of scalars like [1,2,3] and operations between matrices (lists of vectors), where a vector could appear as [[1,2,3]], or alternatively as [[1],[2],[3]]. Really, please pay attention to this distinction. In the second situation there is no confusion. [[1,2,3]] is a row vector and [[1],[2],[3]] is a column vector. The operations all behave as expected. No problem. But I suspect you never created an object like [[1],[2],[3]] in OpenSCAD. The way OpenSCAD is designed it is more natural to use row vectors if you are going to work with objects like these. I don't know why multmatrix() was chosen to use the column vector convention when the rest of the design seems to be pointing towards row vectors. But maybe you prefer to work with the first type of object, a vector like [1,2,3] that is simply a list. Is this a row vector or a column vector? Well, and this is important: there is no way to tell. You cannot "transpose" it because what would the transposed object look like? How can you "transpose" the list [1,2,3] into some other list that is still a vector but not the same? You cannot. So the decision is arbitrary. A mathematician would say you could tell by whether transformations operate on the left or the right. But that doesn't work in this case because OpenSCAD treats a list [1,2,3] as a column vector if you multiply on the left and a row vector if you multiply on the right. In other words, both M*v and v*transpose(M) are legal. Both compute the same thing. In the former case v is a column vector and in the latter it is a row vector. I said above that row vectors are the natural way to use OpenSCAD. Consider your transform_path function which you wrote as a loop that invokes transform. I would be inclined to write this using a single matrix product rather than a loop of matrix vector products like this: function transform_path(path,m) = path*transpose(m); This seems more elegant and I might imagine it would be faster on a long path, though I haven't tested that code. Here path is an array of points that are row vectors and we are applying the transformation on the right, unfortunately with the obligatory transpose. On Tue, 2 Apr 2019 at 18:25, adrianv <avm4@> wrote: > I experimented a bit with these operations and then I also read the > manual. > :) Matrix*matrix products work exactly as expected, when both matrices > are > lists of lists. What is perhaps a bit trickier is the handling of > vectors, > where a vector is a list of scalars. > > Note that there is no meaningful notion of "row vector" vs "column vector" > for a list of scalars, nor is there any meaningful notion of > transpose---these objects are just vectors. So I can't make sense of > "transpose(v)" as an OpenSCAD operation on a vector, v. How would it > change? > > Simply put, when you do matrix*vector or vector*matrix operations, > OpenSCAD > treats the vector as either a column or row as needed to make the > operation > possible. > > So in OpenSCAD, M*v = v*transpose(M). But if you want to transform a list > of points, then you have to pay attention to the orientation of the data, > so > M*[v1,v2,v3,v4,...] will fail because you have now organized the data as a > matrix of row vectors, whereas multiplying on the right (by the transpose) > will work: [v1,v2,v3,v4,...]*transpose(M). And of course it would also > work to do M*transpose([v1,v2,v3,v4,...]) but then the result would need > to > be transposed again to get a list of points. > > > Ronaldo wrote > > Mathematically, the product of a matrix by a row matrix is undefined > > (except if the matrix has just one column) but in OpenSCAD, the vector > is > > considered a column vector instead of a row vector in the product of a > > matrix by a vector. On the other hand, the product of a vector by a > matrix > > in OpenSCAD has the same meaning it has in Mathematics. So, in > > Mathematics, > > the following holds for a row vector v and matrix M : > > > > v * M = transpose(M)*transpose(v) > > > > but in OpenSCAD we get an undefined vector in the evaluation of the > right > > expression. > > > > > > -- > Sent from: http://forum.openscad.org/ > > _______________________________________________ > OpenSCAD mailing list > Discuss@.openscad > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org > _______________________________________________ OpenSCAD mailing list Discuss@.openscad http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Thanks. Yes it was me that had it all wrong. It all makes sense now. Can you transpose a row vector and get a column vector? Please show me. It appears that I was not clear in my explanation of how OpenSCAD works. I meant mathematically, or on paper. OpenSCAD doesn't have anyway of differentiating singe dimensions vectors. As you say matrix multiplication just picks the appropriate one, so the net result is it transposes m. I think this is what led to my confusion. If you use single row or column matrices then multiplication with a matrix is only happy if the correct one is passed. It gives undef for a column on the left or a row on the right and gives the correct form of output vector. It is all explicit and clear but the results are single row or column matrices, so not very useful. I don't think there is any distinction in maths: one dimensional matrices are vectors. It is just computer science where matrices are often lists of lists and vectors are just lists I would be inclined to write this using a single matrix product rather than a loop of matrix vector products like this: Yes I think that might be quicker but it produces vec4s and polyhedron only seems to accept vec3. Stripping the ones with a for loop after makes it slower. On Tue, 2 Apr 2019 at 23:31, adrianv <[hidden email]> wrote: nophead wrote _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
On Wed, Apr 03, 2019 at 11:22:54AM +0100, nop head wrote:
> I don't think there is any distinction in > maths: one dimensional matrices are vectors. It is just computer science > where matrices are often lists of lists and vectors are just lists No. In math there is a difference between column vectors and row-vectors. If you multiply a column vector with a row vector one way and you get a matrix, multiply them the other way around, you get a scalar. It is computer science that cannot simply distiguish between the two: a vector is a vector. As you say: you can make an Nx1 and an 1xN matrices to show the difference. Roger. -- ** [hidden email] ** https://www.BitWizard.nl/ ** +31-15-2049110 ** ** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 ** The plan was simple, like my brother-in-law Phil. But unlike Phil, this plan just might work. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
I meant there are no 1xn or nx1 matrices in maths, they are just row or column vectors. On Wed, 3 Apr 2019 at 11:36, Rogier Wolff <[hidden email]> wrote: On Wed, Apr 03, 2019 at 11:22:54AM +0100, nop head wrote: _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
On Wed, Apr 03, 2019 at 11:48:44AM +0100, nop head wrote:
> I meant there are no 1xn or nx1 matrices in maths, they are just row or > column vectors. Oh, ok. I think in maths a 1xN or Nx1 matrix is precisely equivalent to the corresponding vector. Everything is a matrix, what is commonly known as a vector is a matrix with one dimension "1". What is commonly known as a "scalar" is a matrix with both dimensions "1".... Roger. -- ** [hidden email] ** https://www.BitWizard.nl/ ** +31-15-2049110 ** ** Delftechpark 11 2628 XJ Delft, The Netherlands. KVK: 27239233 ** The plan was simple, like my brother-in-law Phil. But unlike Phil, this plan just might work. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
It is... er... matter of definitions.
If scalar was 1x1 matrix you could not multiply MxN matrix by scalar. On 4/3/2019 1:53 PM, Rogier Wolff wrote: > On Wed, Apr 03, 2019 at 11:48:44AM +0100, nop head wrote: >> I meant there are no 1xn or nx1 matrices in maths, they are just row or >> column vectors. > > Oh, ok. I think in maths a 1xN or Nx1 matrix is precisely equivalent > to the corresponding vector. > > Everything is a matrix, what is commonly known as a vector is a matrix > with one dimension "1". What is commonly known as a "scalar" is a > matrix with both dimensions "1".... > > Roger. > _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Let me try to summarize this discussion regarding OpenSCAD codes: a) matrices in OpenSCAD are list of lists of scalars; simple vectors (list of scalars) are not matrices; b) as such, vectors can't be transposed like matrices; c) in products v*M, where v is a vector and M an appropriate matrix, v may be regarded as a row matrix; d) in products M*v, v may be regarded as a column matrix; e) v*M == T(M)*v and v*T(M) == M*v where T is the transpose operator; f) v*w is a scalar when v and w are vectors of same dimension. g) the product of a matrix 1xn by a matrix nXm is a matrix 1xm which is not a m-dimensional vector; h) T(Q*R) = T(R)*T(Q) for any matrix Q nXm and matrix R mXk where T is the transpose operator. So, OpenSCAD matrix multiplications agree the mathematical equivalent. The differences between math and OpenSCAD are just the a), b) and d) above. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
True but in maths a "row vector" and a "row matrix" are alternative names for the exactly same thing, similarly with column vector and column matrix. In OpenSCAD they are different things. So you can pass an actual row matrix as the first operand of a multiplication by a matrix and receive a row matrix back, or you can pass a vector that will be treated as a row vector and get a vector back. So using "row matrix" and column matrix" in c) and d) could be a little confusing. On Wed, 3 Apr 2019 at 16:25, Ronaldo Persiano <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
In reply to this post by nophead
nop head <[hidden email]> wrote:
Striping the ones may be avoided if the matrices are 3x4. The operator multmatrix() ignores the last row and accepts 3x4 matrices so if you drop the last row of your matrices, multmatrix() will work as expected. However, to apply a 3x4 matrix to a list of 4D points, we will need to transpose the matrix:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Ronaldo wrote
> nop head < > nop.head@ > > wrote: > >> I would be inclined to write this using a single matrix product rather >>> than a loop of matrix vector products like this: >>> function transform_path(path,m) = path*transpose(m); >>> >> >> Yes I think that might be quicker but it produces vec4s and polyhedron >> only seems to accept vec3. Stripping the ones with a for loop after makes >> it slower. >> > > Striping the ones may be avoided if the matrices are 3x4. The operator > multmatrix() ignores the last row and accepts 3x4 matrices so if you drop > the last row of your matrices, multmatrix() will work as expected. > However, > to apply a 3x4 matrix to a list of 4D points, we will need to transpose > the > matrix: > > [for(i=[0:len(p)-1]) p[i]] * transpose(M) Wouldn't simply p*transpose(M) be better, where M is 3x4? -- Sent from: http://forum.openscad.org/ _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Wouldn't simply It is a pity that my emphasis that p is a list of vectors has been misunderstood. _______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
A list of vec4s is also an 4 x n matrix. So you can post multiply by 3 x 4 matrix and get a 3 x n matrix which is a list of vec3s. On Thu, 4 Apr 2019 at 15:28, Ronaldo Persiano <[hidden email]> wrote:
_______________________________________________ OpenSCAD mailing list [hidden email] http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org |
Free forum by Nabble | Edit this page |