discuss@lists.openscad.org

OpenSCAD general discussion Mailing-list

View all threads

Some thoughts on deriving world coordinates for objects

JB
Jordan Brown
Fri, Oct 23, 2020 2:48 AM

[ Note:  In March 2019, I said this wasn't possible.  I was wrong. ]

Definition:  "world coordinates" are the resulting "universe" coordinate
system of the whole model, the coordinate system that the displayed axes
are relative to.  They are distinct from the local coordinate system
inside a transformed object, and from the device coordinates used to
represent the model on a display device.

People frequently ask about various problems that involve things like
measuring distances between points on a model.

There are two tough problems in these questions:
1)  OpenSCAD modules are black holes; information goes in but no
information comes out.  You cannot extract any information about the
contents of the module.  But it can echo, and maybe that's helpful. 
Anyhow, I'm not thinking about this part of the problem.

2)  Modules are mostly opaque to data coming in, too.  The object
doesn't know what its ancestors will do to it.  A cube() thinks that it
has a corner at [0,0,0], but usually it will be translated and rotated
so that it doesn't, and it doesn't have any way to either reverse that
transformation or evaluate it to determine its world coordinates.

It's this latter that I've been musing about.

There's no intrinsic reason why an object can't know its own
transformation.  After all, it's called through a tree of
transformations.  Its parents could tell it the transformation.

Consider these redefinitions of some standard primitives:

// Initially, the local coordinates are the same as world coordinates.
$mm = [
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [0,0,0,1]
];

// Given a point in local coordinates, return its location in world coordinates.
function whereis(p3) =
    let(r4 = $mm * [p3.x, p3.y, p3.z, 1])
    [ r4.x, r4.y, r4.z];

// Translate, but tracking the transformation.
module translate(delta) {
    m = [
        [1,0,0,delta.x],
        [0,1,0,delta.y],
        [0,0,1,delta.z],
        [0,0,0,1]
    ];
    $mm = $mm * m;
    multmatrix(m) children();
}

// Rotate, tracking transformations.
module rotate(a) {
    a = is_num(a) ? [ 0,0,a ] : a;
    echo(a);
    rx = [
        [1, 0,        0,         0],
        [0, cos(a.x), -sin(a.x), 0],
        [0, sin(a.x), cos(a.x),  0],
        [0, 0,        0,         1]
    ];
    ry = [
        [ cos(a.y),  0, sin(a.y), 0],
        [ 0,         1, 0,        0],
        [ -sin(a.y), 0, cos(a.y), 0],
        [ 0,         0, 0,        1]
    ];
    rz = [
        [ cos(a.z), -sin(a.z), 0, 0],
        [ sin(a.z), cos(a.z),  0, 0],
        [ 0,        0,         1, 0],
        [ 0,        0,         0, 1]
    ];
    m = rx*ry*rz;
    $mm = $mm * m;
    multmatrix(m) children();
}

// Scale, tracking transformations
module scale(s) {
    s = is_num(s) ? [ s,s,s ] : s;
    m = [
        [ s.x, 0,   0,   0 ],
        [ 0,   s.y, 0,   0 ],
        [ 0,   0,   s.z, 0 ],
        [ 0,   0,   0,   1 ]
    ];
    $mm = $mm * m;
    multmatrix(m) children();
}

scale(2) rotate(45) translate([0,5,0]) translate([10,0,0]) {
    cube();
    echo(whereis([0,0,0]));
}

These "transformation-tracking" transformation modules give their
children a transformation matrix $mm that is the transformation from
local coordinates to world coordinates.  This allows the child to, for
any point in its local coordinate system, determine (and presumably emit
via echo()) the corresponding world coordinates for that point.

For extra fun, let's give translate() another feature:

$rm = $mm;

// Translate, but tracking the transformation and reverse transformation.
module translate(delta) {
    m = [
        [1,0,0,delta.x],
        [0,1,0,delta.y],
        [0,0,1,delta.z],
        [0,0,0,1]
    ];
    $mm = $mm * m;
    rm = [
        [1,0,0,-delta.x],
        [0,1,0,-delta.y],
        [0,0,1,-delta.z],
        [0,0,0,1]
    ];
    $rm = $rm * rm;
    multmatrix(m) children();
}

and add a new module:

module origin() {
    multmatrix($rm) children();
}

Now if we do this:

translate([0,5,0]) translate([10,0,0]) {
    cube();
    origin() cube();
}

We get a cube that's out at [10,5,0] and another cube that's at the
origin, and the cube at the origin stays there no matter what we do to
the enclosing transformations.

(Equivalent extensions to rotate() and scale() left as an exercise for
the reader.)

Net, you would be able to determine world coordinates, and you'd be able
to position things in world coordinates even from inside a transformed
coordinate system.

This is similar to schemes that others have devised where you maintain a
transformation matrix "by hand", and then you apply it to your points
and objects.  This scheme does it within the normal structure of
OpenSCAD transformations.

Note that while I've done this "in user mode", it could be implemented
inside OpenSCAD's built-in transformations.  (OCD:  Compatibly, give or
take the namespace intrusions.)

Problem:  scale with any axis having a scale of zero can't be reversed.

Problem:  reversing multmatrix itself is a matrix inversion, isn't
trivial.  (But it doesn't seem awful either.  Might be best to have
origin() invert $mm, rather than having multmatrix invert its argument.)

Combined with the $vxx variables, I think you could do things like have
label floating in a constant place on the screen, with an arrow pointing
at a particular point on the model.

You could not directly measure the distance between two points on the
model, because of the "module is a black hole" effect.  (Special case: 
you can measure relative to an object that is the current object's
ancestor, because black holes only keep information from leaving.) 
Similarly, you could not position an object relative to an object that
is not its ancestor.

Does anybody else think that there might be interesting possibilities here?

[ Note:  In March 2019, I said this wasn't possible.  I was wrong. ] Definition:  "world coordinates" are the resulting "universe" coordinate system of the whole model, the coordinate system that the displayed axes are relative to.  They are distinct from the local coordinate system inside a transformed object, and from the device coordinates used to represent the model on a display device. People frequently ask about various problems that involve things like measuring distances between points on a model. There are two tough problems in these questions: 1)  OpenSCAD modules are black holes; information goes in but no information comes out.  You cannot extract any information about the contents of the module.  But it can echo, and maybe that's helpful.  Anyhow, I'm not thinking about this part of the problem. 2)  Modules are mostly opaque to data coming *in*, too.  The object doesn't know what its ancestors will do to it.  A cube() thinks that it has a corner at [0,0,0], but usually it will be translated and rotated so that it doesn't, and it doesn't have any way to either reverse that transformation or evaluate it to determine its world coordinates. It's this latter that I've been musing about. There's no intrinsic reason why an object can't know its own transformation.  After all, it's called through a tree of transformations.  Its parents could tell it the transformation. Consider these redefinitions of some standard primitives: // Initially, the local coordinates are the same as world coordinates. $mm = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; // Given a point in local coordinates, return its location in world coordinates. function whereis(p3) = let(r4 = $mm * [p3.x, p3.y, p3.z, 1]) [ r4.x, r4.y, r4.z]; // Translate, but tracking the transformation. module translate(delta) { m = [ [1,0,0,delta.x], [0,1,0,delta.y], [0,0,1,delta.z], [0,0,0,1] ]; $mm = $mm * m; multmatrix(m) children(); } // Rotate, tracking transformations. module rotate(a) { a = is_num(a) ? [ 0,0,a ] : a; echo(a); rx = [ [1, 0, 0, 0], [0, cos(a.x), -sin(a.x), 0], [0, sin(a.x), cos(a.x), 0], [0, 0, 0, 1] ]; ry = [ [ cos(a.y), 0, sin(a.y), 0], [ 0, 1, 0, 0], [ -sin(a.y), 0, cos(a.y), 0], [ 0, 0, 0, 1] ]; rz = [ [ cos(a.z), -sin(a.z), 0, 0], [ sin(a.z), cos(a.z), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; m = rx*ry*rz; $mm = $mm * m; multmatrix(m) children(); } // Scale, tracking transformations module scale(s) { s = is_num(s) ? [ s,s,s ] : s; m = [ [ s.x, 0, 0, 0 ], [ 0, s.y, 0, 0 ], [ 0, 0, s.z, 0 ], [ 0, 0, 0, 1 ] ]; $mm = $mm * m; multmatrix(m) children(); } scale(2) rotate(45) translate([0,5,0]) translate([10,0,0]) { cube(); echo(whereis([0,0,0])); } These "transformation-tracking" transformation modules give their children a transformation matrix $mm that is the transformation from local coordinates to world coordinates.  This allows the child to, for any point in its local coordinate system, determine (and presumably emit via echo()) the corresponding world coordinates for that point. For extra fun, let's give translate() another feature: $rm = $mm; // Translate, but tracking the transformation and reverse transformation. module translate(delta) { m = [ [1,0,0,delta.x], [0,1,0,delta.y], [0,0,1,delta.z], [0,0,0,1] ]; $mm = $mm * m; rm = [ [1,0,0,-delta.x], [0,1,0,-delta.y], [0,0,1,-delta.z], [0,0,0,1] ]; $rm = $rm * rm; multmatrix(m) children(); } and add a new module: module origin() { multmatrix($rm) children(); } Now if we do this: translate([0,5,0]) translate([10,0,0]) { cube(); origin() cube(); } We get a cube that's out at [10,5,0] and another cube that's at the origin, and the cube at the origin stays there no matter what we do to the enclosing transformations. (Equivalent extensions to rotate() and scale() left as an exercise for the reader.) Net, you would be able to determine world coordinates, and you'd be able to position things in world coordinates even from inside a transformed coordinate system. This is similar to schemes that others have devised where you maintain a transformation matrix "by hand", and then you apply it to your points and objects.  This scheme does it within the normal structure of OpenSCAD transformations. Note that while I've done this "in user mode", it could be implemented inside OpenSCAD's built-in transformations.  (OCD:  Compatibly, give or take the namespace intrusions.) Problem:  scale with any axis having a scale of zero can't be reversed. Problem:  reversing multmatrix itself is a matrix inversion, isn't trivial.  (But it doesn't seem awful either.  Might be best to have origin() invert $mm, rather than having multmatrix invert its argument.) Combined with the $vxx variables, I think you could do things like have label floating in a constant place on the screen, with an arrow pointing at a particular point on the model. You could *not* directly measure the distance between two points on the model, because of the "module is a black hole" effect.  (Special case:  you can measure relative to an object that is the current object's ancestor, because black holes only keep information from leaving.)  Similarly, you could not position an object relative to an object that is not its ancestor. Does anybody else think that there might be interesting possibilities here?
JB
Jordan Brown
Fri, Oct 23, 2020 3:00 AM

On 10/22/2020 7:48 PM, Jordan Brown wrote:

[ Note:  In March 2019, I said this wasn't possible.  I was wrong. ]

For anybody who is curious about the mistake that I made back then...

I had assumed that a module yielded an object based on its parameters,
period.  That object could then be transformed, after it was created. 
Because it could be transformed after it was created, it couldn't
possibly know the relationship between its local coordinate system and
the world coordinate system, because at the time it was created that
relationship wasn't known.

There are perfectly reasonable graphics systems that use this model.

But that's not how OpenSCAD objects are created.  They are always
created in the context of their ancestral tree of transformations.  That
tree exists before the object is constructed.  In addition, that tree
can pass information down into the object in $ variables.

As a result, it is possible for an OpenSCAD object to know how its
local coordinate system relates to the world coordinate system.

On 10/22/2020 7:48 PM, Jordan Brown wrote: > > [ Note:  In March 2019, I said this wasn't possible.  I was wrong. ] > For anybody who is curious about the mistake that I made back then... I had assumed that a module yielded an object based on its parameters, period.  That object could then be transformed, *after* it was created.  Because it could be transformed after it was created, it couldn't possibly know the relationship between its local coordinate system and the world coordinate system, because at the time it was created that relationship wasn't known. There are perfectly reasonable graphics systems that use this model. But that's not how OpenSCAD objects are created.  They are always created in the context of their ancestral tree of transformations.  That tree exists *before* the object is constructed.  In addition, that tree can pass information down into the object in $ variables. As a result, it *is* possible for an OpenSCAD object to know how its local coordinate system relates to the world coordinate system.
DM
Doug Moen
Fri, Oct 23, 2020 3:27 AM

It sounds similar to the Relativity library:
https://github.com/davidson16807/relativity.scad

On Thu, Oct 22, 2020, at 10:48 PM, Jordan Brown wrote:

[ Note:  In March 2019, I said this wasn't possible.  I was wrong. ]

Definition:  "world coordinates" are the resulting "universe" coordinate system of the whole model, the coordinate system that the displayed axes are relative to.  They are distinct from the local coordinate system inside a transformed object, and from the device coordinates used to represent the model on a display device.

People frequently ask about various problems that involve things like measuring distances between points on a model.

There are two tough problems in these questions:

  1. OpenSCAD modules are black holes; information goes in but no information comes out.  You cannot extract any information about the contents of the module.  But it can echo, and maybe that's helpful.  Anyhow, I'm not thinking about this part of the problem.

  2. Modules are mostly opaque to data coming in, too.  The object doesn't know what its ancestors will do to it.  A cube() thinks that it has a corner at [0,0,0], but usually it will be translated and rotated so that it doesn't, and it doesn't have any way to either reverse that transformation or evaluate it to determine its world coordinates.

It's this latter that I've been musing about.

There's no intrinsic reason why an object can't know its own transformation.  After all, it's called through a tree of transformations.  Its parents could tell it the transformation.

Consider these redefinitions of some standard primitives:

// Initially, the local coordinates are the same as world coordinates.

$mm = [
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]
];

// Given a point in local coordinates, return its location in world coordinates.
function whereis(p3) =
let(r4 = $mm * [p3.x, p3.y, p3.z, 1])
[ r4.x, r4.y, r4.z];

// Translate, but tracking the transformation.
module translate(delta) {
m = [
[1,0,0,delta.x],
[0,1,0,delta.y],
[0,0,1,delta.z],
[0,0,0,1]
];
$mm = $mm * m;
multmatrix(m) children();
}

// Rotate, tracking transformations.
module rotate(a) {
a = is_num(a) ? [ 0,0,a ] : a;
echo(a);
rx = [
[1, 0,        0,        0],
[0, cos(a.x), -sin(a.x), 0],
[0, sin(a.x), cos(a.x),  0],
[0, 0,        0,        1]
];
ry = [
[ cos(a.y),  0, sin(a.y), 0],
[ 0,        1, 0,        0],
[ -sin(a.y), 0, cos(a.y), 0],
[ 0,        0, 0,        1]
];
rz = [
[ cos(a.z), -sin(a.z), 0, 0],
[ sin(a.z), cos(a.z),  0, 0],
[ 0,        0,        1, 0],
[ 0,        0,        0, 1]
];
m = rxryrz;
$mm = $mm * m;
multmatrix(m) children();
}

// Scale, tracking transformations
module scale(s) {
s = is_num(s) ? [ s,s,s ] : s;
m = [
[ s.x, 0,  0,  0 ],
[ 0,  s.y, 0,  0 ],
[ 0,  0,  s.z, 0 ],
[ 0,  0,  0,  1 ]
];
$mm = $mm * m;
multmatrix(m) children();
}

scale(2) rotate(45) translate([0,5,0]) translate([10,0,0]) {
cube();
echo(whereis([0,0,0]));
}

These "transformation-tracking" transformation modules give their children a transformation matrix $mm that is the transformation from local coordinates to world coordinates.  This allows the child to, for any point in its local coordinate system, determine (and presumably emit via echo()) the corresponding world coordinates for that point.

For extra fun, let's give translate() another feature:

$rm = $mm;

// Translate, but tracking the transformation and reverse transformation.
module translate(delta) {
m = [
[1,0,0,delta.x],
[0,1,0,delta.y],
[0,0,1,delta.z],
[0,0,0,1]
];
$mm = $mm * m;
rm = [
[1,0,0,-delta.x],
[0,1,0,-delta.y],
[0,0,1,-delta.z],
[0,0,0,1]
];
$rm = $rm * rm;
multmatrix(m) children();
}

and add a new module:

module origin() {

multmatrix($rm) children();

}

Now if we do this:

translate([0,5,0]) translate([10,0,0]) {

cube();
origin() cube();

}

We get a cube that's out at [10,5,0] and another cube that's at the origin, and the cube at the origin stays there no matter what we do to the enclosing transformations.

(Equivalent extensions to rotate() and scale() left as an exercise for the reader.)

Net, you would be able to determine world coordinates, and you'd be able to position things in world coordinates even from inside a transformed coordinate system.

This is similar to schemes that others have devised where you maintain a transformation matrix "by hand", and then you apply it to your points and objects.  This scheme does it within the normal structure of OpenSCAD transformations.

Note that while I've done this "in user mode", it could be implemented inside OpenSCAD's built-in transformations.  (OCD:  Compatibly, give or take the namespace intrusions.)

Problem:  scale with any axis having a scale of zero can't be reversed.

Problem:  reversing multmatrix itself is a matrix inversion, isn't trivial.  (But it doesn't seem awful either.  Might be best to have origin() invert $mm, rather than having multmatrix invert its argument.)

Combined with the $vxx variables, I think you could do things like have label floating in a constant place on the screen, with an arrow pointing at a particular point on the model.

You could not directly measure the distance between two points on the model, because of the "module is a black hole" effect.  (Special case:  you can measure relative to an object that is the current object's ancestor, because black holes only keep information from leaving.)  Similarly, you could not position an object relative to an object that is not its ancestor.

Does anybody else think that there might be interesting possibilities here?


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

It sounds similar to the Relativity library: https://github.com/davidson16807/relativity.scad On Thu, Oct 22, 2020, at 10:48 PM, Jordan Brown wrote: > [ Note: In March 2019, I said this wasn't possible. I was wrong. ] > Definition: "world coordinates" are the resulting "universe" coordinate system of the whole model, the coordinate system that the displayed axes are relative to. They are distinct from the local coordinate system inside a transformed object, and from the device coordinates used to represent the model on a display device. > > People frequently ask about various problems that involve things like measuring distances between points on a model. > > There are two tough problems in these questions: > 1) OpenSCAD modules are black holes; information goes in but no information comes out. You cannot extract any information about the contents of the module. But it can echo, and maybe that's helpful. Anyhow, I'm not thinking about this part of the problem. > > 2) Modules are mostly opaque to data coming *in*, too. The object doesn't know what its ancestors will do to it. A cube() thinks that it has a corner at [0,0,0], but usually it will be translated and rotated so that it doesn't, and it doesn't have any way to either reverse that transformation or evaluate it to determine its world coordinates. > > It's this latter that I've been musing about. > > There's no intrinsic reason why an object can't know its own transformation. After all, it's called through a tree of transformations. Its parents could tell it the transformation. > > Consider these redefinitions of some standard primitives: > >> // Initially, the local coordinates are the same as world coordinates. $mm = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; // Given a point in local coordinates, return its location in world coordinates. function whereis(p3) = let(r4 = $mm * [p3.x, p3.y, p3.z, 1]) [ r4.x, r4.y, r4.z]; // Translate, but tracking the transformation. module translate(delta) { m = [ [1,0,0,delta.x], [0,1,0,delta.y], [0,0,1,delta.z], [0,0,0,1] ]; $mm = $mm * m; multmatrix(m) children(); } // Rotate, tracking transformations. module rotate(a) { a = is_num(a) ? [ 0,0,a ] : a; echo(a); rx = [ [1, 0, 0, 0], [0, cos(a.x), -sin(a.x), 0], [0, sin(a.x), cos(a.x), 0], [0, 0, 0, 1] ]; ry = [ [ cos(a.y), 0, sin(a.y), 0], [ 0, 1, 0, 0], [ -sin(a.y), 0, cos(a.y), 0], [ 0, 0, 0, 1] ]; rz = [ [ cos(a.z), -sin(a.z), 0, 0], [ sin(a.z), cos(a.z), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; m = rx*ry*rz; $mm = $mm * m; multmatrix(m) children(); } // Scale, tracking transformations module scale(s) { s = is_num(s) ? [ s,s,s ] : s; m = [ [ s.x, 0, 0, 0 ], [ 0, s.y, 0, 0 ], [ 0, 0, s.z, 0 ], [ 0, 0, 0, 1 ] ]; $mm = $mm * m; multmatrix(m) children(); } scale(2) rotate(45) translate([0,5,0]) translate([10,0,0]) { cube(); echo(whereis([0,0,0])); } >> > These "transformation-tracking" transformation modules give their children a transformation matrix $mm that is the transformation from local coordinates to world coordinates. This allows the child to, for any point in its local coordinate system, determine (and presumably emit via echo()) the corresponding world coordinates for that point. > For extra fun, let's give translate() another feature: >> $rm = $mm; // Translate, but tracking the transformation and reverse transformation. module translate(delta) { m = [ [1,0,0,delta.x], [0,1,0,delta.y], [0,0,1,delta.z], [0,0,0,1] ]; $mm = $mm * m; rm = [ [1,0,0,-delta.x], [0,1,0,-delta.y], [0,0,1,-delta.z], [0,0,0,1] ]; $rm = $rm * rm; multmatrix(m) children(); } >> > and add a new module: >> module origin() { multmatrix($rm) children(); } >> > Now if we do this: >> translate([0,5,0]) translate([10,0,0]) { cube(); origin() cube(); } >> > We get a cube that's out at [10,5,0] and another cube that's at the origin, and the cube at the origin stays there no matter what we do to the enclosing transformations. > (Equivalent extensions to rotate() and scale() left as an exercise for the reader.) > Net, you would be able to determine world coordinates, and you'd be able to position things in world coordinates even from inside a transformed coordinate system. > This is similar to schemes that others have devised where you maintain a transformation matrix "by hand", and then you apply it to your points and objects. This scheme does it within the normal structure of OpenSCAD transformations. > Note that while I've done this "in user mode", it could be implemented inside OpenSCAD's built-in transformations. (OCD: Compatibly, give or take the namespace intrusions.) > Problem: scale with any axis having a scale of zero can't be reversed. > Problem: reversing multmatrix itself is a matrix inversion, isn't trivial. (But it doesn't seem awful either. Might be best to have origin() invert $mm, rather than having multmatrix invert its argument.) > Combined with the $vxx variables, I think you could do things like have label floating in a constant place on the screen, with an arrow pointing at a particular point on the model. > You could *not* directly measure the distance between two points on the model, because of the "module is a black hole" effect. (Special case: you can measure relative to an object that is the current object's ancestor, because black holes only keep information from leaving.) Similarly, you could not position an object relative to an object that is not its ancestor. > > Does anybody else think that there might be interesting possibilities here? > > > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org >
JB
Jordan Brown
Fri, Oct 23, 2020 4:35 AM

On 10/22/2020 8:27 PM, Doug Moen wrote:

Not really.  Relativity (from a brief look) is about constructing
objects connected to one another.

This concept is about managing the relationship between local
coordinates and world coordinates.

The two are orthogonal to one another.

On 10/22/2020 8:27 PM, Doug Moen wrote: > It sounds similar to the Relativity library: > https://github.com/davidson16807/relativity.scad > <https://github.com/davidson16807/relativity.scad> Not really.  Relativity (from a brief look) is about constructing objects connected to one another. This concept is about managing the relationship between local coordinates and world coordinates. The two are orthogonal to one another.
RW
Rob Ward
Fri, Oct 23, 2020 8:00 AM

This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers).  Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction.

Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me.

But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable.

I have sympathies on both sides.
Cheers, RobW

On 23 October 2020 3:35:06 pm AEDT, Jordan Brown openscad@jordan.maileater.net wrote:

On 10/22/2020 8:27 PM, Doug Moen wrote:

Not really.  Relativity (from a brief look) is about constructing
objects connected to one another.

This concept is about managing the relationship between local
coordinates and world coordinates.

The two are orthogonal to one another.

This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers). Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction. Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me. But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable. I have sympathies on both sides. Cheers, RobW On 23 October 2020 3:35:06 pm AEDT, Jordan Brown <openscad@jordan.maileater.net> wrote: >On 10/22/2020 8:27 PM, Doug Moen wrote: >> It sounds similar to the Relativity library: >> https://github.com/davidson16807/relativity.scad >> <https://github.com/davidson16807/relativity.scad> > >Not really.  Relativity (from a brief look) is about constructing >objects connected to one another. > >This concept is about managing the relationship between local >coordinates and world coordinates. > >The two are orthogonal to one another.
RD
Revar Desmera
Fri, Oct 23, 2020 8:26 AM

Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local.

-Revar

On Oct 23, 2020, at 1:01 AM, Rob Ward rl.ward@bigpond.com wrote:

 This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers). Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction.

Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me.

But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable.

I have sympathies on both sides.
Cheers, RobW

On 23 October 2020 3:35:06 pm AEDT, Jordan Brown openscad@jordan.maileater.net wrote:

On 10/22/2020 8:27 PM, Doug Moen wrote:
It sounds similar to the Relativity library:
https://github.com/davidson16807/relativity.scad

Not really.  Relativity (from a brief look) is about constructing objects connected to one another.

This concept is about managing the relationship between local coordinates and world coordinates.

The two are orthogonal to one another.

Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local. -Revar > On Oct 23, 2020, at 1:01 AM, Rob Ward <rl.ward@bigpond.com> wrote: > >  This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers). Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction. > > Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me. > > But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable. > > I have sympathies on both sides. > Cheers, RobW > >> On 23 October 2020 3:35:06 pm AEDT, Jordan Brown <openscad@jordan.maileater.net> wrote: >>> On 10/22/2020 8:27 PM, Doug Moen wrote: >>> It sounds similar to the Relativity library: >>> https://github.com/davidson16807/relativity.scad >> Not really. Relativity (from a brief look) is about constructing objects connected to one another. >> >> This concept is about managing the relationship between local coordinates and world coordinates. >> >> The two are orthogonal to one another. >> > _______________________________________________ > OpenSCAD mailing list > Discuss@lists.openscad.org > http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
A
arnholm@arnholm.org
Fri, Oct 23, 2020 8:43 AM

On 2020-10-23 04:48, Jordan Brown wrote:

There's no intrinsic reason why an object can't know its own
transformation.  After all, it's called through a tree of
transformations.  Its parents could tell it the transformation.

If you want to be a purist you might say that an object and its
transformation(s) are separate, the transformation defines the context
and an object can exist in several contexts at the same time. I come
from the tradition of Finite Element Analysis and super-element
techniques where this is important. A super-element can thus not know
its transformation, because it exists in multiple contexts on multiple
levels, and the same super-element might be "re-tracked" in several
contexts.

However, in Constructive Solid Geometry (CSG) this way of looking at
things is not so central, and unlike for Finite Element super-elements a
CSG object is typically copied with each transformation, so each copy
can thus know its transformation. This is exactly what happens in
AngelCAD. Also, since 3d objects are named variables there, they can be
used to return some information. Any 2d or 3d object inherits from
"shape" ( https://arnholm.github.io/angelcad-docs/docs/classshape.html )
and a shape can return its transformation in the form of a tmatrix
object which is essentially just a 4x4 homogenous transformation matrix
like in your code examples

Problem:  reversing multmatrix itself is a matrix inversion, isn't
trivial.  (But it doesn't seem awful either.  Might be best to have
origin() invert $mm, rather than having multmatrix invert its
argument.)

General NxN matrix inversion isn't trivial, but the special case of 4x4
inversion is pretty much trivial and can be coded symbolically.

Carsten Arnholm

On 2020-10-23 04:48, Jordan Brown wrote: > There's no intrinsic reason why an object can't know its own > transformation. After all, it's called through a tree of > transformations. Its parents could tell it the transformation. If you want to be a purist you might say that an object and its transformation(s) are separate, the transformation defines the context and an object can exist in several contexts at the same time. I come from the tradition of Finite Element Analysis and super-element techniques where this is important. A super-element can thus not know its transformation, because it exists in multiple contexts on multiple levels, and the same super-element might be "re-tracked" in several contexts. However, in Constructive Solid Geometry (CSG) this way of looking at things is not so central, and unlike for Finite Element super-elements a CSG object is typically copied with each transformation, so each copy can thus know its transformation. This is exactly what happens in AngelCAD. Also, since 3d objects are named variables there, they can be used to return some information. Any 2d or 3d object inherits from "shape" ( https://arnholm.github.io/angelcad-docs/docs/classshape.html ) and a shape can return its transformation in the form of a tmatrix object which is essentially just a 4x4 homogenous transformation matrix like in your code examples > Problem: reversing multmatrix itself is a matrix inversion, isn't > trivial. (But it doesn't seem awful either. Might be best to have > origin() invert $mm, rather than having multmatrix invert its > argument.) General NxN matrix inversion isn't trivial, but the special case of 4x4 inversion is pretty much trivial and can be coded symbolically. Carsten Arnholm
RD
Revar Desmera
Fri, Oct 23, 2020 11:32 AM

This seems to work:

$local_matrix = undef;

function _wc_transpose(m) = [
    for (c=[0:3]) [
        for (r=[0:3]) m[r][c]
    ]
];

function _wc_minor(m,r,c) = [
    for (rr=[0:1:len(m)-1])
    if (rr != r) [
        for (cc=[0:1:len(m[rr])-1])
        if (cc != c) 
        m[rr][cc]
    ]
];

function _wc_sum(v,_total=0,_i=0) =
    _i>=len(v) ? _total :
    _wc_sum(v, _total+v[_i], _i+1);

function _wc_determinant(m) =
    len(m)==2? m[0][0] * m[1][1] - m[0][1] * m[1][0] :
    _wc_sum([
        for (c = [0:1:len(m[0])-1])
        ((c%2==0)? 1 : -1) * m[0][c] *
        _wc_determinant(_wc_minor(m,0,c))
    ]);

function _wc_inverse(m) =
    let(
        det = _wc_determinant(m),
        inv = _wc_transpose([
            for (r = [0:3]) [
                for (c = [0:3])
                ((r+c) % 2 == 0? 1 : -1) *
                _wc_determinant(_wc_minor(m,r,c))
            ]
        ]) / det
    ) inv;


function _wc_ident() = [
	[1, 0, 0, 0],
	[0, 1, 0, 0],
	[0, 0, 1, 0],
	[0, 0, 0, 1]
];

function _wc_translate(v) = [
    [1, 0, 0, v.x],
    [0, 1, 0, v.y],
    [0, 0, 1, v.z],
    [0 ,0, 0,   1]
];

function _wc_scale(v) = [
    [v.x,   0,   0, 0],
    [  0, v.y,   0, 0],
    [  0,   0, v.z, 0],
    [  0,   0,   0, 1]
];

function _wc_rotx(ang) = [
    [1,        0,         0,   0],
    [0, cos(ang), -sin(ang),   0],
    [0, sin(ang),  cos(ang),   0],
    [0,        0,         0,   1]
];

function _wc_roty(ang) = [
    [ cos(ang), 0, sin(ang),   0],
    [        0, 1,        0,   0],
    [-sin(ang), 0, cos(ang),   0],
    [        0, 0,        0,   1]
];

function _wc_rotz(ang) = [
    [cos(ang), -sin(ang), 0, 0],
    [sin(ang),  cos(ang), 0, 0],
    [       0,         0, 1, 0],
    [       0,         0, 0, 1]
];

function _wc_rot_by_axis(u, ang) =
    ang==0? _wc_ident() :
    let(
        u = u/norm(u),
        c = cos(ang),
        c2 = 1-c,
        s = sin(ang)
    ) [
        [u.x*u.x*c2+c    , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0],
        [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c    , u.y*u.z*c2-u.x*s, 0],
        [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c    , 0],
        [               0,                0,                0, 1]
    ];

function _wc_vec_angle(v1,v2) =
    let(
        n0 = norm(v1),
        n1 = norm(v2)
    )
    assert(n0>0 && n1>0, "Zero length vector.")
    let (
        c1 = (v1*v2)/(n0*n1),
        c2 = min(max(c1,-1),1) // Correct for FP rounding errors.
    ) acos(c2);
 
function _wc_to3d(v) = [for (i=[0:2]) i<len(v)? v[i] : 0];

function _wc_vec_axis(v1,v2) =
        let(
          eps = 1e-6,
          w1 = _wc_to3d(v1/norm(v1)),
          w2 = _wc_to3d(v2/norm(v2)),
          w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2 
               : (norm([abs(w2.x),abs(w2.y),abs(w2.z)]-[0,0,1]) > eps)? [0,0,1] 
               : [1,0,0],
          x = cross(w1,w3)
        ) x/norm(x);


module translate(v) {
    multmat(_wc_translate(v)) children();
}


module scale(v) {
    multmat(_wc_scale(v)) children();
}


module rotate(a=0, v) {
    mat = is_undef(v)? (
        is_list(a)? (
            _wc_rotx(len(a)>=1? assert(is_num(a.x)) a.x : 0) *
            _wc_roty(len(a)>=2? assert(is_num(a.y)) a.y : 0) *
            _wc_rotz(len(a)>=3? assert(is_num(a.z)) a.z : 0)
        ) : (
            assert(is_num(a)) _wc_rotz(a)
        )
    ) : (
        assert(is_num(a))
        assert(is_list(v))
        let( v = [ for (i=[0:2]) v[i] ] )
        _wc_rot_by_axis(v, a)
    );
    multmat(mat) children();
}


module multmat(mat) {
    $local_matrix = is_undef($local_matrix)? mat : ($local_matrix * mat);
    multmatrix(mat) children();
}


// Returns the local transformation matrix.
function local_matrix() =
    is_undef($local_matrix)? _wc_ident() :
    $local_matrix;


// Returns the local translation vector [X,Y,Z]
function local_translation() =
    is_undef($local_matrix)? [0,0,0] :
    let(
        vec = [0, 0, 0, 1],
        lvec = $local_matrix * vec
    ) _wc_to3d(lvec);


// If mat is undef, resets to the world reference frame.
// If given a matrix in mat, resets to that reference frame.
module reference_frame(mat) {
    if (is_undef(mat)) {
        multmat(_wc_inverse($local_matrix)) children();
    } else {
        multmat(_wc_inverse(mat)) children();
    }
}

translate([30,40,50]) {
    scale([2,3,4]) {
        rotate(30,v=[1,1,0]) {
            cylinder(d1=5,d2=0,h=5);
            reference_frame() {
                cylinder(d1=5,d2=0,h=5);  // This gets rendered in the world reference frame
            }
        }
    }
}

You should be able to also store local reference frames in the middle there, in $special_vars and pass those to reference_frame() to access arbitrary reference frames.  You just have to make sure that your code never calls multmatrix(), but multmat() instead.

  • Revar

On Oct 23, 2020, at 1:26 AM, Revar Desmera revarbat@gmail.com wrote:

Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local.

-Revar

On Oct 23, 2020, at 1:01 AM, Rob Ward rl.ward@bigpond.com wrote:

 This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers). Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction.

Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me.

But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable.

I have sympathies on both sides.
Cheers, RobW

On 23 October 2020 3:35:06 pm AEDT, Jordan Brown openscad@jordan.maileater.net wrote:
On 10/22/2020 8:27 PM, Doug Moen wrote:

Not really.  Relativity (from a brief look) is about constructing objects connected to one another.

This concept is about managing the relationship between local coordinates and world coordinates.

The two are orthogonal to one another.


OpenSCAD mailing list
Discuss@lists.openscad.org
http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org

This seems to work: ```openscad $local_matrix = undef; function _wc_transpose(m) = [ for (c=[0:3]) [ for (r=[0:3]) m[r][c] ] ]; function _wc_minor(m,r,c) = [ for (rr=[0:1:len(m)-1]) if (rr != r) [ for (cc=[0:1:len(m[rr])-1]) if (cc != c) m[rr][cc] ] ]; function _wc_sum(v,_total=0,_i=0) = _i>=len(v) ? _total : _wc_sum(v, _total+v[_i], _i+1); function _wc_determinant(m) = len(m)==2? m[0][0] * m[1][1] - m[0][1] * m[1][0] : _wc_sum([ for (c = [0:1:len(m[0])-1]) ((c%2==0)? 1 : -1) * m[0][c] * _wc_determinant(_wc_minor(m,0,c)) ]); function _wc_inverse(m) = let( det = _wc_determinant(m), inv = _wc_transpose([ for (r = [0:3]) [ for (c = [0:3]) ((r+c) % 2 == 0? 1 : -1) * _wc_determinant(_wc_minor(m,r,c)) ] ]) / det ) inv; function _wc_ident() = [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ]; function _wc_translate(v) = [ [1, 0, 0, v.x], [0, 1, 0, v.y], [0, 0, 1, v.z], [0 ,0, 0, 1] ]; function _wc_scale(v) = [ [v.x, 0, 0, 0], [ 0, v.y, 0, 0], [ 0, 0, v.z, 0], [ 0, 0, 0, 1] ]; function _wc_rotx(ang) = [ [1, 0, 0, 0], [0, cos(ang), -sin(ang), 0], [0, sin(ang), cos(ang), 0], [0, 0, 0, 1] ]; function _wc_roty(ang) = [ [ cos(ang), 0, sin(ang), 0], [ 0, 1, 0, 0], [-sin(ang), 0, cos(ang), 0], [ 0, 0, 0, 1] ]; function _wc_rotz(ang) = [ [cos(ang), -sin(ang), 0, 0], [sin(ang), cos(ang), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; function _wc_rot_by_axis(u, ang) = ang==0? _wc_ident() : let( u = u/norm(u), c = cos(ang), c2 = 1-c, s = sin(ang) ) [ [u.x*u.x*c2+c , u.x*u.y*c2-u.z*s, u.x*u.z*c2+u.y*s, 0], [u.y*u.x*c2+u.z*s, u.y*u.y*c2+c , u.y*u.z*c2-u.x*s, 0], [u.z*u.x*c2-u.y*s, u.z*u.y*c2+u.x*s, u.z*u.z*c2+c , 0], [ 0, 0, 0, 1] ]; function _wc_vec_angle(v1,v2) = let( n0 = norm(v1), n1 = norm(v2) ) assert(n0>0 && n1>0, "Zero length vector.") let ( c1 = (v1*v2)/(n0*n1), c2 = min(max(c1,-1),1) // Correct for FP rounding errors. ) acos(c2); function _wc_to3d(v) = [for (i=[0:2]) i<len(v)? v[i] : 0]; function _wc_vec_axis(v1,v2) = let( eps = 1e-6, w1 = _wc_to3d(v1/norm(v1)), w2 = _wc_to3d(v2/norm(v2)), w3 = (norm(w1-w2) > eps && norm(w1+w2) > eps) ? w2 : (norm([abs(w2.x),abs(w2.y),abs(w2.z)]-[0,0,1]) > eps)? [0,0,1] : [1,0,0], x = cross(w1,w3) ) x/norm(x); module translate(v) { multmat(_wc_translate(v)) children(); } module scale(v) { multmat(_wc_scale(v)) children(); } module rotate(a=0, v) { mat = is_undef(v)? ( is_list(a)? ( _wc_rotx(len(a)>=1? assert(is_num(a.x)) a.x : 0) * _wc_roty(len(a)>=2? assert(is_num(a.y)) a.y : 0) * _wc_rotz(len(a)>=3? assert(is_num(a.z)) a.z : 0) ) : ( assert(is_num(a)) _wc_rotz(a) ) ) : ( assert(is_num(a)) assert(is_list(v)) let( v = [ for (i=[0:2]) v[i] ] ) _wc_rot_by_axis(v, a) ); multmat(mat) children(); } module multmat(mat) { $local_matrix = is_undef($local_matrix)? mat : ($local_matrix * mat); multmatrix(mat) children(); } // Returns the local transformation matrix. function local_matrix() = is_undef($local_matrix)? _wc_ident() : $local_matrix; // Returns the local translation vector [X,Y,Z] function local_translation() = is_undef($local_matrix)? [0,0,0] : let( vec = [0, 0, 0, 1], lvec = $local_matrix * vec ) _wc_to3d(lvec); // If mat is undef, resets to the world reference frame. // If given a matrix in mat, resets to that reference frame. module reference_frame(mat) { if (is_undef(mat)) { multmat(_wc_inverse($local_matrix)) children(); } else { multmat(_wc_inverse(mat)) children(); } } translate([30,40,50]) { scale([2,3,4]) { rotate(30,v=[1,1,0]) { cylinder(d1=5,d2=0,h=5); reference_frame() { cylinder(d1=5,d2=0,h=5); // This gets rendered in the world reference frame } } } } ``` You should be able to also store local reference frames in the middle there, in $special_vars and pass those to reference_frame() to access arbitrary reference frames. You just have to make sure that your code never calls multmatrix(), but multmat() instead. - Revar > On Oct 23, 2020, at 1:26 AM, Revar Desmera <revarbat@gmail.com> wrote: > > Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local. > > -Revar > > >> On Oct 23, 2020, at 1:01 AM, Rob Ward <rl.ward@bigpond.com> wrote: >> >>  This is assuming there is some reverse logic provided at the time the forward logic of an openscad design is conceived. Unfortunately for people who ask these questions about openscad the original designers of the scripts (let's not call it a programming language until it can do X=X+1) only looked forward to what it could produce (ie parts for 3D printers). Not how it it could be introspective, and analyse itself. Its current powers are a noble and awesome achievement for sure, and should be applauded, but in this race to simply produce "things" lies its main restriction. >> >> Those of you who desire to work your way back through the 3-D machinations of your design to reliable coordinates in a defined universe will be ultimately disappointed. I am not, because I don't aspire to these heights of intellectual achievement. Design it, print it for me. >> >> But I do understand what you desire, however only a total rewrite of openscad could achieve that. There are too many hours invested by the people who have built Openscad in the way it is useful for them now, to invest the intellectual effort you improve it beyond what will make most of their previous designs unworkable. >> >> I have sympathies on both sides. >> Cheers, RobW >> >> On 23 October 2020 3:35:06 pm AEDT, Jordan Brown <openscad@jordan.maileater.net> wrote: >> On 10/22/2020 8:27 PM, Doug Moen wrote: >>> It sounds similar to the Relativity library: >>> https://github.com/davidson16807/relativity.scad <https://github.com/davidson16807/relativity.scad> >> Not really. Relativity (from a brief look) is about constructing objects connected to one another. >> >> This concept is about managing the relationship between local coordinates and world coordinates. >> >> The two are orthogonal to one another. >> >> _______________________________________________ >> OpenSCAD mailing list >> Discuss@lists.openscad.org >> http://lists.openscad.org/mailman/listinfo/discuss_lists.openscad.org
JB
Jordan Brown
Fri, Oct 23, 2020 3:58 PM

On 10/23/2020 1:43 AM, arnholm@arnholm.org wrote:

On 2020-10-23 04:48, Jordan Brown wrote:

There's no intrinsic reason why an object can't know its own
transformation.  After all, it's called through a tree of
transformations.  Its parents could tell it the transformation.

If you want to be a purist you might say that an object and its
transformation(s) are separate, the transformation defines the context
and an object can exist in several contexts at the same time.

See my separate message on the mistake that I made in March 2019.  That
was how I was thinking, that an object was a black box to which a
transformation was applied.

But that's not what OpenSCAD does.  I wouldn't say that it "copies" the
object, but rather that each invocation creates a new instance of the
object, in the context of its transformation.

Neither scheme is wrong... they're just different.

On 10/23/2020 1:43 AM, arnholm@arnholm.org wrote: > On 2020-10-23 04:48, Jordan Brown wrote: >> There's no intrinsic reason why an object can't know its own >> transformation.  After all, it's called through a tree of >> transformations.  Its parents could tell it the transformation. > If you want to be a purist you might say that an object and its > transformation(s) are separate, the transformation defines the context > and an object can exist in several contexts at the same time. See my separate message on the mistake that I made in March 2019.  That was how I was thinking, that an object was a black box to which a transformation was applied. But that's not what OpenSCAD does.  I wouldn't say that it "copies" the object, but rather that each invocation creates a new instance of the object, in the context of its transformation. Neither scheme is wrong... they're just different.
JB
Jordan Brown
Fri, Oct 23, 2020 4:04 PM

On 10/23/2020 1:00 AM, Rob Ward wrote:

This is assuming there is some reverse logic provided at the time the
forward logic of an openscad design is conceived.

But, for almost all transformations, there is.

When you say

translate([10,0,0]) ...

the inverse is

translate([-10,0,0]) ...

and similarly for any other transformation, except scale(0) variants.

The trick is in keeping track of those transformations and their
inverses as you work your way through the stack of transformations.

Note that I'm not attempting to "undo" Boolean operations... just
transformations.

On 10/23/2020 1:00 AM, Rob Ward wrote: > This is assuming there is some reverse logic provided at the time the > forward logic of an openscad design is conceived. But, for almost all transformations, there is. When you say translate([10,0,0]) ... the inverse is translate([-10,0,0]) ... and similarly for any other transformation, except scale(0) variants. The trick is in keeping track of those transformations and their inverses as you work your way through the stack of transformations. Note that I'm not attempting to "undo" Boolean operations... just transformations.
JB
Jordan Brown
Fri, Oct 23, 2020 4:15 PM

On 10/23/2020 1:26 AM, Revar Desmera wrote:

Actually if you want to go to the effort of overriding all the
transformation built-ins (translate, rotate, scale) with user modules
calling multmatrix(), and provide an alternate call for multmatrix()
itself, it should become fairly trivial to keep track of the world
coordinates compared to local.

Yes.  I did that, mostly, but you did even more of it, and you supplied
the matrix invert that I hadn't done yet.  (Mostly because I haven't
done a matrix invert in >40 years...)

Cool!  Thanks!

I haven't tried it, but I suspect that you have a bug in
reference_frame().  When you supply a "mat" argument to it, I think you
need to first multiply by $local_matrix to get back to world
coordinates, before multiplying by the specified matrix to get to the
desired reference frame.

On 10/23/2020 1:26 AM, Revar Desmera wrote: > Actually if you want to go to the effort of overriding all the > transformation built-ins (translate, rotate, scale) with user modules > calling multmatrix(), and provide an alternate call for multmatrix() > itself, it should become fairly trivial to keep track of the world > coordinates compared to local. Yes.  I did that, mostly, but you did even more of it, and you supplied the matrix invert that I hadn't done yet.  (Mostly because I haven't done a matrix invert in >40 years...) Cool!  Thanks! I haven't tried it, but I suspect that you have a bug in reference_frame().  When you supply a "mat" argument to it, I think you need to first multiply by $local_matrix to get back to world coordinates, before multiplying by the specified matrix to get to the desired reference frame.
JB
Jordan Brown
Fri, Oct 23, 2020 4:16 PM

On 10/22/2020 7:48 PM, Jordan Brown wrote:

Combined with the $vxx variables, I think you could do things like
have label floating in a constant place on the screen, with an arrow
pointing at a particular point on the model.

And indeed, I've got a test program that does exactly that.

Can anybody point me at how to take an OpenSCAD animation sequence and
turn it into an animated GIF?

I did run into an ... interesting ... variable scoping issue. I'll
send a separate message on that.

On 10/22/2020 7:48 PM, Jordan Brown wrote: > Combined with the $vxx variables, I think you could do things like > have label floating in a constant place on the screen, with an arrow > pointing at a particular point on the model. And indeed, I've got a test program that does exactly that. Can anybody point me at how to take an OpenSCAD animation sequence and turn it into an animated GIF? I *did* run into an ... interesting ... variable scoping issue. I'll send a separate message on that.
RD
Revar Desmera
Fri, Oct 23, 2020 8:15 PM

I’m afraid I didn’t see your code until after I’d posted, but yeah, that looks like the same idea.

You’re right about the bug in reference_frame().  I wrote this code off the top of my head, and in parts cannibalized from my BOSL2 library at 3am.  Only minimal testing was actually done.  Proof of concept or some such.  The matrix inverse I implemented based on https://www.mathsisfun.com/algebra/matrix-inverse-minors-cofactors-adjugate.html

I suspect I can re-implement this code to not even need the matrix inverse code, if we construct the inverse matrix as we go.  Either by keeping a stack of the individual transforms, or by reversing the matrix multiplications in another $var.  Not sure if that works, but I think it will.

I had also written code to get the local scaling and rotation as well as the local translation, but I’m pretty sure those will get screwed up by any skew transforms.

  • Revar

On Oct 23, 2020, at 9:15 AM, Jordan Brown openscad@jordan.maileater.net wrote:

On 10/23/2020 1:26 AM, Revar Desmera wrote:

Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local.

Yes.  I did that, mostly, but you did even more of it, and you supplied the matrix invert that I hadn't done yet.  (Mostly because I haven't done a matrix invert in >40 years...)

Cool!  Thanks!

I haven't tried it, but I suspect that you have a bug in reference_frame().  When you supply a "mat" argument to it, I think you need to first multiply by $local_matrix to get back to world coordinates, before multiplying by the specified matrix to get to the desired reference frame.

I’m afraid I didn’t see your code until after I’d posted, but yeah, that looks like the same idea. You’re right about the bug in reference_frame(). I wrote this code off the top of my head, and in parts cannibalized from my BOSL2 library at 3am. Only minimal testing was actually done. Proof of concept or some such. The matrix inverse I implemented based on https://www.mathsisfun.com/algebra/matrix-inverse-minors-cofactors-adjugate.html I suspect I can re-implement this code to not even need the matrix inverse code, if we construct the inverse matrix as we go. Either by keeping a stack of the individual transforms, or by reversing the matrix multiplications in another $var. Not sure if that works, but I think it will. I had also written code to get the local scaling and rotation as well as the local translation, but I’m pretty sure those will get screwed up by any skew transforms. - Revar > On Oct 23, 2020, at 9:15 AM, Jordan Brown <openscad@jordan.maileater.net> wrote: > > On 10/23/2020 1:26 AM, Revar Desmera wrote: >> Actually if you want to go to the effort of overriding all the transformation built-ins (translate, rotate, scale) with user modules calling multmatrix(), and provide an alternate call for multmatrix() itself, it should become fairly trivial to keep track of the world coordinates compared to local. > > Yes. I did that, mostly, but you did even more of it, and you supplied the matrix invert that I hadn't done yet. (Mostly because I haven't done a matrix invert in >40 years...) > > Cool! Thanks! > > I haven't tried it, but I suspect that you have a bug in reference_frame(). When you supply a "mat" argument to it, I think you need to first multiply by $local_matrix to get back to world coordinates, before multiplying by the specified matrix to get to the desired reference frame. >
JB
Jordan Brown
Fri, Oct 23, 2020 8:27 PM

On 10/23/2020 1:15 PM, Revar Desmera wrote:

I suspect I can re-implement this code to not even need the matrix inverse code, if we construct the inverse matrix as we go.

That's what I was doing, but when you get to a multmatrix() you need to
invert it.  Since my guess is that multmatrix is more common than
resetting to the world coordinate system, it seems like it'd be better
to do the invert at the end.

I had also written code to get the local scaling and rotation as well as the local translation, but I’m pretty sure those will get screwed up by any skew transforms.

Yes.

On 10/23/2020 1:15 PM, Revar Desmera wrote: > I suspect I can re-implement this code to not even need the matrix inverse code, if we construct the inverse matrix as we go. That's what I was doing, but when you get to a multmatrix() you need to invert it.  Since my guess is that multmatrix is more common than resetting to the world coordinate system, it seems like it'd be better to do the invert at the end. > I had also written code to get the local scaling and rotation as well as the local translation, but I’m pretty sure those will get screwed up by any skew transforms. Yes.
JB
Jordan Brown
Tue, Oct 27, 2020 7:04 AM

On 10/23/2020 9:16 AM, Jordan Brown wrote:

On 10/22/2020 7:48 PM, Jordan Brown wrote:

Combined with the $vxx variables, I think you could do things like
have label floating in a constant place on the screen, with an arrow
pointing at a particular point on the model.

And indeed, I've got a test program that does exactly that.

So there's several things going on in that animation (assuming that it
works - it's my first time)...

There's a cube, translated away from the origin, orbiting around the Z axis.
There's a cube created inside that translation and rotation, that's
locked back to the origin.
The camera is orbiting around the scene.
There's a label stuck at a fixed location on the display.
There's an arrow from the label to the orbiting cube.

Here's the source.  I don't claim that this is the cleanest or best way
to do this... it's just a proof of concept.


// Initially, the local coordinates are the same as world coordinates.
$mm = [
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]
];

$rm = $mm;

// Given a point in local coordinates, return its location in world coordinates.
function whereis(p3) =
let(r4 = $mm * [p3.x, p3.y, p3.z, 1])
[ r4.x, r4.y, r4.z];

// Translate, but tracking the transformation and reverse transformation.
module translate(delta) {
m = [
[1,0,0,delta.x],
[0,1,0,delta.y],
[0,0,1,delta.z],
[0,0,0,1]
];
$mm = $mm * m;
rm = [
[1,0,0,-delta.x],
[0,1,0,-delta.y],
[0,0,1,-delta.z],
[0,0,0,1]
];
$rm = rm * $rm;
multmatrix(m) children();
}

// Rotate, tracking transformations.
module rotate(a) {
a = is_num(a) ? [ 0,0,a ] : a;
rx = [
[1, 0,        0,        0],
[0, cos(a.x), -sin(a.x), 0],
[0, sin(a.x), cos(a.x),  0],
[0, 0,        0,        1]
];
ry = [
[ cos(a.y),  0, sin(a.y), 0],
[ 0,        1, 0,        0],
[ -sin(a.y), 0, cos(a.y), 0],
[ 0,        0, 0,        1]
];
rz = [
[ cos(a.z), -sin(a.z), 0, 0],
[ sin(a.z), cos(a.z),  0, 0],
[ 0,        0,        1, 0],
[ 0,        0,        0, 1]
];
m = rxryrz;
$mm = $mm * m;

 b = -a;
 rbx = [
     [1, 0,        0,         0],
     [0, cos(b.x), -sin(b.x), 0],
     [0, sin(b.x), cos(b.x),  0],
     [0, 0,        0,         1]
 ];
 rby = [
     [ cos(b.y),  0, sin(b.y), 0],
     [ 0,         1, 0,        0],
     [ -sin(b.y), 0, cos(b.y), 0],
     [ 0,         0, 0,        1]
 ];
 rbz = [
     [ cos(b.z), -sin(b.z), 0, 0],
     [ sin(b.z), cos(b.z),  0, 0],
     [ 0,        0,         1, 0],
     [ 0,        0,         0, 1]
 ];
 rm = rbz*rby*rbx;
 $rm = rm * $rm;
 multmatrix(m) children();

}

// Scale, tracking transformations
module scale(s) {
s = is_num(s) ? [ s,s,s ] : s;
m = [
[ s.x, 0,  0,  0 ],
[ 0,  s.y, 0,  0 ],
[ 0,  0,  s.z, 0 ],
[ 0,  0,  0,  1 ]
];
$mm = $mm * m;
rm = [
[ 1/s.x, 0,    0,    0 ],
[ 0,    1/s.y, 0,    0 ],
[ 0,    0,    1/s.z, 0 ],
[ 0,    0,    0,    1 ]
];
$rm = rm * $rm;

 multmatrix(m) children();

}

// Lock the children to the viewport.
// Undoes any model transforms and viewport transforms.
// I don't know if I really have the $vpd interaction correct.
module vp() {
r = $vpr;
origin() translate($vpt) rotate([0,0,r.z]) rotate([0,r.y,0]) rotate([r.x,0,0]) scale($vpd/250) children();
}

// Reset the transform for children to the world coordinate
// system, undoing any model transforms.
module origin() {
multmatrix($rm) {
// And, since we're back at the basic transformation,
// reset our record of the transforms.
$mm = [
[1,0,0,0],
[0,1,0,0],
[0,0,1,0],
[0,0,0,1]
];

     $rm = $mm;

     children();
 }

}

// Given an [x,y] or [x,y,z], transform to a
// [rho, theta] or [rho, theta, phi]
function topolar(p) =
len(p) == 3 ? topolar3(p) : topolar2(p);

function topolar2(p) = [
norm(p),
atan2(p.y, p.x)
];

function topolar3(p) = [
norm(p),
atan2(p.y, p.x),
atan2(norm([p.x,p.y]), p.z)
];

// Draw a line (a cylinder) from p1 to p2, with a diameter of d.
// Shorten the p1 end by off0 and the p2 end by off1.
// Add arrowheads of length h0 at the p1 and and h1 at the p2 end.
module line(p1, p2, d, off0, off1, h0, h1) {
off0 = off0 ? off0 : 0;
off1 = off1 ? off1 : 0;
translate(p1) {
polar = topolar(p2-p1);
rho = polar[0];
theta = polar[1];
phi = polar[2];
rotate([0,0,theta])
rotate([0,phi,0])
translate([0,0,off0]) {
if (h0) {
cylinder(d1=d,d2=3d, h=h0);
}
if (h1) {
translate([0,0,rho-off1-off0-h1])
cylinder(d1=3
d, d2=d, h=h1);
}
cylinder(d=d, h=rho-off1-off0);
}
}
}

// OK, let's set up a demo.
// Take a cube.
// Scale it up.  Translate and rotate it so that it orbits the origin.
// Inside that transform, reset back to the original world
// coordinates and place another cube at the origin.
// Have the camera orbit the origin.
// Add a label at a fixed place on the viewport.
// Add an arrow from the label to a point on the orbiting cube.
$vpr = [$vpr[0],$vpr[1],360*$t];
$vpt = [0,0,0];

scale(2) rotate(2360$t) translate([0,5,0]) translate([10,0,0]) group() {
cube(3);
p1 = whereis([0,0,3]);
origin() cube(3);
vp() translate([30,20,0]) group() {
p2 = whereis([0,0,0]);
text("cube", valign="center");
origin() line(p1,p2,d=0.5,off0=2, off1=2, h0=2);
}
}

On 10/23/2020 9:16 AM, Jordan Brown wrote: > On 10/22/2020 7:48 PM, Jordan Brown wrote: >> Combined with the $vxx variables, I think you could do things like >> have label floating in a constant place on the screen, with an arrow >> pointing at a particular point on the model. > > And indeed, I've got a test program that does exactly that. So there's several things going on in that animation (assuming that it works - it's my first time)... There's a cube, translated away from the origin, orbiting around the Z axis. There's a cube created *inside* that translation and rotation, that's locked back to the origin. The camera is orbiting around the scene. There's a label stuck at a fixed location on the display. There's an arrow from the label to the orbiting cube. Here's the source.  I don't claim that this is the cleanest or best way to do this... it's just a proof of concept. --- // Initially, the local coordinates are the same as world coordinates. $mm = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; $rm = $mm; // Given a point in local coordinates, return its location in world coordinates. function whereis(p3) = let(r4 = $mm * [p3.x, p3.y, p3.z, 1]) [ r4.x, r4.y, r4.z]; // Translate, but tracking the transformation and reverse transformation. module translate(delta) { m = [ [1,0,0,delta.x], [0,1,0,delta.y], [0,0,1,delta.z], [0,0,0,1] ]; $mm = $mm * m; rm = [ [1,0,0,-delta.x], [0,1,0,-delta.y], [0,0,1,-delta.z], [0,0,0,1] ]; $rm = rm * $rm; multmatrix(m) children(); } // Rotate, tracking transformations. module rotate(a) { a = is_num(a) ? [ 0,0,a ] : a; rx = [ [1, 0, 0, 0], [0, cos(a.x), -sin(a.x), 0], [0, sin(a.x), cos(a.x), 0], [0, 0, 0, 1] ]; ry = [ [ cos(a.y), 0, sin(a.y), 0], [ 0, 1, 0, 0], [ -sin(a.y), 0, cos(a.y), 0], [ 0, 0, 0, 1] ]; rz = [ [ cos(a.z), -sin(a.z), 0, 0], [ sin(a.z), cos(a.z), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; m = rx*ry*rz; $mm = $mm * m; b = -a; rbx = [ [1, 0, 0, 0], [0, cos(b.x), -sin(b.x), 0], [0, sin(b.x), cos(b.x), 0], [0, 0, 0, 1] ]; rby = [ [ cos(b.y), 0, sin(b.y), 0], [ 0, 1, 0, 0], [ -sin(b.y), 0, cos(b.y), 0], [ 0, 0, 0, 1] ]; rbz = [ [ cos(b.z), -sin(b.z), 0, 0], [ sin(b.z), cos(b.z), 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ]; rm = rbz*rby*rbx; $rm = rm * $rm; multmatrix(m) children(); } // Scale, tracking transformations module scale(s) { s = is_num(s) ? [ s,s,s ] : s; m = [ [ s.x, 0, 0, 0 ], [ 0, s.y, 0, 0 ], [ 0, 0, s.z, 0 ], [ 0, 0, 0, 1 ] ]; $mm = $mm * m; rm = [ [ 1/s.x, 0, 0, 0 ], [ 0, 1/s.y, 0, 0 ], [ 0, 0, 1/s.z, 0 ], [ 0, 0, 0, 1 ] ]; $rm = rm * $rm; multmatrix(m) children(); } // Lock the children to the viewport. // Undoes any model transforms and viewport transforms. // I don't know if I really have the $vpd interaction correct. module vp() { r = $vpr; origin() translate($vpt) rotate([0,0,r.z]) rotate([0,r.y,0]) rotate([r.x,0,0]) scale($vpd/250) children(); } // Reset the transform for children to the world coordinate // system, undoing any model transforms. module origin() { multmatrix($rm) { // And, since we're back at the basic transformation, // reset our record of the transforms. $mm = [ [1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1] ]; $rm = $mm; children(); } } // Given an [x,y] or [x,y,z], transform to a // [rho, theta] or [rho, theta, phi] function topolar(p) = len(p) == 3 ? topolar3(p) : topolar2(p); function topolar2(p) = [ norm(p), atan2(p.y, p.x) ]; function topolar3(p) = [ norm(p), atan2(p.y, p.x), atan2(norm([p.x,p.y]), p.z) ]; // Draw a line (a cylinder) from p1 to p2, with a diameter of d. // Shorten the p1 end by off0 and the p2 end by off1. // Add arrowheads of length h0 at the p1 and and h1 at the p2 end. module line(p1, p2, d, off0, off1, h0, h1) { off0 = off0 ? off0 : 0; off1 = off1 ? off1 : 0; translate(p1) { polar = topolar(p2-p1); rho = polar[0]; theta = polar[1]; phi = polar[2]; rotate([0,0,theta]) rotate([0,phi,0]) translate([0,0,off0]) { if (h0) { cylinder(d1=d,d2=3*d, h=h0); } if (h1) { translate([0,0,rho-off1-off0-h1]) cylinder(d1=3*d, d2=d, h=h1); } cylinder(d=d, h=rho-off1-off0); } } } // OK, let's set up a demo. // Take a cube. // Scale it up. Translate and rotate it so that it orbits the origin. // *Inside* that transform, reset back to the original world // coordinates and place another cube at the origin. // Have the camera orbit the origin. // Add a label at a fixed place on the viewport. // Add an arrow from the label to a point on the orbiting cube. $vpr = [$vpr[0],$vpr[1],360*$t]; $vpt = [0,0,0]; scale(2) rotate(2*360*$t) translate([0,5,0]) translate([10,0,0]) group() { cube(3); p1 = whereis([0,0,3]); origin() cube(3); vp() translate([30,20,0]) group() { p2 = whereis([0,0,0]); text("cube", valign="center"); origin() line(p1,p2,d=0.5,off0=2, off1=2, h0=2); } }
R
rickan
Tue, Oct 27, 2020 12:46 PM

I think this is way cool!

I'm unable to find group() in

https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language

but I suppose it does nothing more than ensure its parent sees a single
child?

--
Sent from: http://forum.openscad.org/

I think this is way cool! I'm unable to find group() in https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language but I suppose it does nothing more than ensure its parent sees a single child? -- Sent from: http://forum.openscad.org/
JB
Jordan Brown
Tue, Oct 27, 2020 3:12 PM

On 10/27/2020 5:46 AM, rickan wrote:

I think this is way cool!

I'm unable to find group() in

https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language

but I suppose it does nothing more than ensure its parent sees a single
child?

Yes, though the precise need is to defer execution of the assignments in
the block until the parent runs.  (Ref my message " 'interesting'
scoping behavior for $ variables" of 10/23/2020 at 9:48am Pacific.)

I should have swapped it out for something documented like union().

On 10/27/2020 5:46 AM, rickan wrote: > I think this is way cool! > > I'm unable to find group() in > > https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/The_OpenSCAD_Language > > but I suppose it does nothing more than ensure its parent sees a single > child? Yes, though the precise need is to defer execution of the assignments in the block until the parent runs.  (Ref my message " 'interesting' scoping behavior for $ variables" of 10/23/2020 at 9:48am Pacific.) I should have swapped it out for something documented like union().
J
jon
Tue, Oct 27, 2020 9:22 PM

Very cool, Jordan!

On 10/27/2020 3:04 AM, Jordan Brown wrote:

On 10/23/2020 9:16 AM, Jordan Brown wrote:

On 10/22/2020 7:48 PM, Jordan Brown wrote:

Combined with the $vxx variables, I think you could do things like
have label floating in a constant place on the screen, with an arrow
pointing at a particular point on the model.

And indeed, I've got a test program that does exactly that.

Very cool, Jordan! On 10/27/2020 3:04 AM, Jordan Brown wrote: > On 10/23/2020 9:16 AM, Jordan Brown wrote: >> On 10/22/2020 7:48 PM, Jordan Brown wrote: >>> Combined with the $vxx variables, I think you could do things like >>> have label floating in a constant place on the screen, with an arrow >>> pointing at a particular point on the model. >> >> And indeed, I've got a test program that does exactly that.
JB
Jordan Brown
Sun, Jul 4, 2021 4:38 PM

On 10/27/2020 8:12 AM, Jordan Brown wrote:

On 10/27/2020 5:46 AM, rickan wrote:

[ use of group() ]

Yes, though the precise need is to defer execution of the assignments
in the block until the parent runs.  (Ref my message " 'interesting'
scoping behavior for $ variables" of 10/23/2020 at 9:48am Pacific.)

And in the current snapshot, child-block assignments are done at the
time that children() is invoked, and so this workaround is no longer
required.

I don't know whether this is really anything more than a toy, but it's a
kind of interesting toy.


For those that for some reason don't remember an exchange from 8 months
ago and don't readily have those messages available: 
http://forum.openscad.org/Some-thoughts-on-deriving-world-coordinates-for-objects-td30377.html

On 10/27/2020 8:12 AM, Jordan Brown wrote: > On 10/27/2020 5:46 AM, rickan wrote: >> [ use of group() ] > Yes, though the precise need is to defer execution of the assignments > in the block until the parent runs.  (Ref my message " 'interesting' > scoping behavior for $ variables" of 10/23/2020 at 9:48am Pacific.) And in the current snapshot, child-block assignments are done at the time that children() is invoked, and so this workaround is no longer required. I don't know whether this is really anything more than a toy, but it's a kind of interesting toy. --- For those that for some reason don't remember an exchange from 8 months ago and don't readily have those messages available:  http://forum.openscad.org/Some-thoughts-on-deriving-world-coordinates-for-objects-td30377.html