.. _vertex_coordinate_debug:
Vertex Coordinate Debug : finding the invertex rotation matrix
================================================================
.. contents:: :local:
Coordinate Systems
-------------------
Collada
~~~~~~~~~
* :google:`Collada Coordinate system`
* http://collada.org/public_forum/showthread.php/828-Coordinate-System-clarification
There is an *UP_AXIS* control in *asset* elements which defaults to *Y_UP*
Geant4
~~~~~~~~
* :google:`geant4 coordinate system`
* http://hypernews.slac.stanford.edu/HyperNews/geant4/get/visualization/521.html left handed system OR not, apparently not
* 2008 : vis looks to one user to be left-handed, +y up, +x right, -z towards viewpoint (not +z toward viewpoint as documented below)
* 2011 : fobs off as optical illusion
* http://geant4.web.cern.ch/geant4/UserDocumentation/UsersGuides/ForApplicationDeveloper/html/ch08s04.html
By default, the up-Vector is parallel to the y-axis and the viewpoint direction
is parallel to the z-axis, so the the view shows the x-axis to the right and
the y-axis upwards - a projection on to the canonical x-y plane
GDML
~~~~~
Coordinates system, same as Geant4 presumably.
VRML2
~~~~~~~
* :google:`VRML2 Coordinate system`
* http://www.mathworks.com/help/sl3d/vrml.html
::
VRML2 coordinate system
+Y
|
|
|
+-------- +X
/
/
/
+Z
The VRML coordinate system is different from the MATLAB and Aerospace Blockset
coordinate systems. VRML uses the world coordinate system in which the y-axis
points upward and the z-axis places objects nearer or farther from the front of
the screen. It is important to realize this fact in situations involving the
interaction of these different coordinate systems. SimMechanics uses the same
coordinate system as VRML.
Rotation angles - In VRML, rotation angles are defined using the right-hand
rule. Imagine your right hand holding an axis while your thumb points in the
direction of the axis toward its positive end. Your four remaining fingers
point in a counterclockwise direction. This counterclockwise direction is the
positive rotation angle of an object moving around that axis.
GDML Geometry Trace
---------------------
N:/data1/env/local/env/geant4/geometry/gdml/g4_01.gdml::
2162 # Z -25000 : +25000
2163 # Z -6005 : +6005 (but shunted down so) -25005 : -12995
2164
2165
2166 # subtracting off the bigger box : is this to avoid numerical issues ???
2167 ## (50-12)/2=19 half dim in Z
2168
2169
2170
* http://lcgapp.cern.ch/project/simu/framework/GDML/doc/GDMLmanual.pdf
The GDML Boolean Solids can be described using following Boolean operations: union,
subtraction and intersection. As for Geant4 Boolean operations, the second solid is placed
with given position and rotation in the system coordinates of the first solid.
::
30919
30920
30921
30922
30923
30924
30925
30926
30927
30928
30929
30930
30931
30932
30933 ## -2400k +2400k box
30934
30935
30936
30937
30938
30939
30940
Debug PV1 `near_rock` vertices
---------------------------------
pycollada Access
~~~~~~~~~~~~~~~~~
::
In [220]: import lxml.etree as ET
In [6]: dae = collada.Collada("0.dae")
In [7]: top = dae.scene.nodes[0]
In [8]: top
Out[8]:
In [11]: boundgeom = list(top.objects("geometry"))
In [12]: len(boundgeom)
Out[12]: 12230
PV0 WorldBox
~~~~~~~~~~~~~~
::
In [151]: boundgeom[0]
Out[151]:
In [162]: for po in list(boundgeom[0].primitives())[0]:print po, po.indices
[0 3 2 1]
[4 7 3 0]
[7 6 2 3]
[6 5 1 2]
[5 4 0 1]
[4 5 6 7]
In [163]: boundgeom[0].original.primitives[0].vertex
Out[163]:
array([[-2400000., -2400000., -2400000.],... ## actually dimensions of boundgeom[0] the worldbox not relevant, just provides the frame
PV1 near_rock untransformed
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
In [179]: boundgeom[1]
Out[179]:
In [194]: boundgeom[1].original.primitives[0].vertex # more relevant, this is what gets transformed
Out[194]:
array([[-25000. , -25000. , 25000. ],
[ 25000. , -25000. , 25000. ],
[ 25000. , 25000. , 25000. ],
[-25000. , 25000. , 25000. ],
[-25000. , 25000. , -12993.79980469],
[-25000. , -25000. , -12993.79980469],
[ 25000. , 25000. , -12993.79980469],
[ 25000. , -25000. , -12993.79980469]], dtype=float32)
In [221]: print ET.tostring(boundgeom[1].original.xmlnode)
-25000 -25000 25000
25000 -25000 25000
25000 25000 25000
-25000 25000 25000
-25000 25000 -12993.8
-25000 -25000 -12993.8
25000 25000 -12993.8
25000 -25000 -12993.8
PV1 near_rock transformation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
In [24]: top.children[0].node.children[1].id
Out[24]: '__dd__Structure__Sites__db-rock0xaa8b0f8'
In [23]: print ET.tostring(top.children[0].node.children[1].transforms[0].xmlnode)
-0.543174 0.83962 0 -16520
-0.83962 -0.543174 0 -802110
0 0 1 -2110
0.0 0.0 0.0 1.0
In [177]: boundgeom[1].matrix[:3,3]
Out[177]: array([ -16520., -802110., -2110.], dtype=float32) # expected translation from GDML
In [315]: collada.scene.makeRotationMatrix(0,0,1,-numpy.pi*122.9/180.) # -122.9 deg is from the GDML
Out[315]:
array([[-0.54317445, 0.83961987, 0. , 0. ],
[-0.83961987, -0.54317445, 0. , 0. ],
[ 0. , 0. , 1. , 0. ],
[ 0. , 0. , 0. , 1. ]], dtype=float32)
In [178]: boundgeom[1].matrix[:3,:3] # rotation anti-clockwise about z axis by -122.9 degrees
Out[178]:
array([[-0.54317403, 0.83961999, 0. ],
[-0.83961999, -0.54317403, 0. ],
[ 0. , 0. , 1. ]], dtype=float32)
In [183]: math.cos(-122.9*math.pi/180.)
Out[183]: -0.54317444995067088
In [184]: math.sin(-122.9*math.pi/180.)
Out[184]: -0.83961986453441306
::
cos th -sin th 0 # th rotation anti-clockwise about z axis
sin th cos th 0
0 0 1
PV1 primitives
~~~~~~~~~~~~~~~~
::
In [197]: for po in boundgeom[1].original.primitives[0]:print po, po.indices
[0 1 2 3]
[4 5 0]
[0 3 4]
[6 4 3]
[3 2 6]
[7 6 2]
[2 1 7]
[5 7 1]
[1 0 5]
[5 4 6]
[6 7 5]
Unsuccessfull diddling
~~~~~~~~~~~~~~~~~~~~~~~
Trying to rotate/reflect things around failed to achieve a PV1 match.
::
In [30]: zrot_ = lambda _:numpy.asmatrix(numpy.array( [[math.cos(_), -math.sin(_), 0],[math.sin(_), math.cos(_), 0],[0, 0, 1]] ))
In [31]: list(boundgeom[1].primitives())[0].vertex * zrot_(math.pi/2.)
Out[31]:
matrix([[-767540.125 , 23931.1484375 , 22890. ],
[-809521.125 , 51089.8515625 , 22890. ],
[-836679.875 , 9108.85058594, 22890. ],
[-794698.875 , -18049.8515625 , 22890. ],
[-794698.875 , -18049.8515625 , -15103.79980469],
[-767540.125 , 23931.1484375 , -15103.79980469],
[-836679.875 , 9108.85058594, -15103.79980469],
[-809521.125 , 51089.8515625 , -15103.79980469]])
In [32]: list(boundgeom[1].primitives())[0].vertex * zrot_(-math.pi/2.)
Out[32]:
matrix([[ 767540.125 , -23931.1484375 , 22890. ],
[ 809521.125 , -51089.8515625 , 22890. ],
[ 836679.875 , -9108.85058594, 22890. ],
[ 794698.875 , 18049.8515625 , 22890. ],
[ 794698.875 , 18049.8515625 , -15103.79980469],
[ 767540.125 , -23931.1484375 , -15103.79980469],
[ 836679.875 , -9108.85058594, -15103.79980469],
[ 809521.125 , -51089.8515625 , -15103.79980469]])
In [35]: xyref = numpy.asmatrix(numpy.array([[0,1,0],[1,0,0],[0,0,1]]))
In [36]: xyref
Out[36]:
matrix([[0, 1, 0],
[1, 0, 0],
[0, 0, 1]])
In [37]: list(boundgeom[1].primitives())[0].vertex * xyref
Out[37]:
matrix([[-767540.125 , -23931.1484375 , 22890. ],
[-809521.125 , -51089.8515625 , 22890. ],
[-836679.875 , -9108.85058594, 22890. ],
[-794698.875 , 18049.8515625 , 22890. ],
[-794698.875 , 18049.8515625 , -15103.79980469],
[-767540.125 , -23931.1484375 , -15103.79980469],
[-836679.875 , -9108.85058594, -15103.79980469],
[-809521.125 , -51089.8515625 , -15103.79980469]])
reconcile PV1 vertices, VRML2 cf pycollada
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
In [269]: C0 = boundgeom[1].original.primitives[0].vertex # collada vertices before transformation
In [270]: C0
Out[270]:
array([[-25000. , -25000. , 25000. ],
[ 25000. , -25000. , 25000. ],
[ 25000. , 25000. , 25000. ],
[-25000. , 25000. , 25000. ],
[-25000. , 25000. , -12993.79980469],
[-25000. , -25000. , -12993.79980469],
[ 25000. , 25000. , -12993.79980469],
[ 25000. , -25000. , -12993.79980469]], dtype=float32)
In [266]: M = numpy.asmatrix(boundgeom[1].matrix).transpose()
In [276]: ( M[:3,:3] * C0.T ).T # transposed collada vertices *pre*-multiplied by the rotation matrix (no translation)
Out[276]: # EUREKA : THIS MATCHES THE VRML2 COORDINATES : "V" below
matrix([[ 34569.8515625 , -7411.14941406, 25000. ],
[ 7411.14941406, 34569.8515625 , 25000. ],
[-34569.8515625 , 7411.14941406, 25000. ],
[ -7411.14941406, -34569.8515625 , 25000. ],
[ -7411.14941406, -34569.8515625 , -12993.79980469],
[ 34569.8515625 , -7411.14941406, -12993.79980469],
[-34569.8515625 , 7411.14941406, -12993.79980469],
[ 7411.14941406, 34569.8515625 , -12993.79980469]], dtype=float32)
In [363]: numpy.dot( C0, boundgeom[1].matrix[:3,:3] ) ## simpler way to do the above avoiding the transposing and keeping post-multiplication
Out[363]:
array([[ 34569.8515625 , -7411.14941406, 25000. ],
[ 7411.14941406, 34569.8515625 , 25000. ],
[-34569.8515625 , 7411.14941406, 25000. ],
[ -7411.14941406, -34569.8515625 , 25000. ],
[ -7411.14941406, -34569.8515625 , -12993.79980469],
[ 34569.8515625 , -7411.14941406, -12993.79980469],
[-34569.8515625 , 7411.14941406, -12993.79980469],
[ 7411.14941406, 34569.8515625 , -12993.79980469]], dtype=float32)
In [287]: C0 * M[:3,:3] # post multiplication (as done by pycollada) leads to vertices that look vaguely similar, with maybe an xy swap,
# but failed to find a rotation + reflection to line them up
# .... the problem was the transpose done to the rotation matrix , sign flip issue
Out[287]:
matrix([[ -7411.14941406, 34569.8515625 , 25000. ],
[-34569.8515625 , -7411.14941406, 25000. ],
[ 7411.14941406, -34569.8515625 , 25000. ],
[ 34569.8515625 , 7411.14941406, 25000. ],
[ 34569.8515625 , 7411.14941406, -12993.79980469],
[ -7411.14941406, 34569.8515625 , -12993.79980469],
[ 7411.14941406, -34569.8515625 , -12993.79980469],
[-34569.8515625 , -7411.14941406, -12993.79980469]], dtype=float32)
PV1 VRML2 coordinates
~~~~~~~~~~~~~~~~~~~~~~~
::
sqlite> select sid,id, x,y,z from wrl.point where sid=1 ;
sid id x y z
---------- ---------- --------------- ---------- ----------
1 0 18049.900390625 -809521.0 22890.0
1 1 -9108.860351562 -767540.0 22890.0
1 2 -51089.8984375 -794699.0 22890.0
1 3 -23931.09960937 -836680.0 22890.0
1 4 -23931.09960937 -836680.0 -15104.200
1 5 18049.900390625 -809521.0 -15104.200
1 6 -51089.8984375 -794699.0 -15104.200
1 7 -9108.860351562 -767540.0 -15104.200
::
simon:~ blyth$ shapedb-shape 1
#---------- SOLID: /dd/Structure/Sites/db-rock.1000
Shape {
appearance Appearance {
material Material {
diffuseColor 1 1 1
transparency 0.7
}
}
geometry IndexedFaceSet {
coord Coordinate {
point [
18049.9 -809521 22890,
-9108.86 -767540 22890,
-51089.9 -794699 22890,
-23931.1 -836680 22890,
-23931.1 -836680 -15104.2,
18049.9 -809521 -15104.2,
-51089.9 -794699 -15104.2,
-9108.86 -767540 -15104.2,
]
}
coordIndex [
0, 1, 2, 3, -1,
4, 5, 0, -1,
0, 3, 4, -1,
6, 4, 3, -1,
3, 2, 6, -1,
7, 6, 2, -1,
2, 1, 7, -1,
5, 7, 1, -1,
1, 0, 5, -1,
5, 4, 6, -1,
6, 7, 5, -1,
]
solid FALSE
}
}
::
In [104]: from env.geant4.geometry.vrml2.vrml2db import VRML2DB
In [105]: db = VRML2DB()
In [286]: a = db.points(1) ; a # VRML2 points from the shape db
Out[286]:
array([[ 18049.90039062, -809521. , 22890. ],
[ -9108.86035156, -767540. , 22890. ],
[ -51089.8984375 , -794699. , 22890. ],
[ -23931.09960938, -836680. , 22890. ],
[ -23931.09960938, -836680. , -15104.20019531],
[ 18049.90039062, -809521. , -15104.20019531],
[ -51089.8984375 , -794699. , -15104.20019531],
[ -9108.86035156, -767540. , -15104.20019531]], dtype=float32)
In [285]: V = a - boundgeom[1].matrix[:3,3] ; V # VRML2 points with translation taken out
Out[285]:
array([[ 34569.8984375 , -7411. , 25000. ],
[ 7411.13964844, 34570. , 25000. ],
[-34569.8984375 , 7411. , 25000. ],
[ -7411.09960938, -34570. , 25000. ],
[ -7411.09960938, -34570. , -12994.20019531],
[ 34569.8984375 , -7411. , -12994.20019531],
[-34569.8984375 , 7411. , -12994.20019531],
[ 7411.13964844, 34570. , -12994.20019531]], dtype=float32)
Transformation Implementations
--------------------------------
PyCollada Transformation Inconsistency
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PyCollada transformations, hmm inconsistent pre/post-multiplication a bug somewhere::
simon:collada blyth$ grep ":3,:3" *.py
light.py: self.position = numpy.dot( matrix[:3,:3], plight.position ) + matrix[:3,3]
light.py: self.direction = numpy.dot( matrix[:3,:3], dlight.direction )
lineset.py: self._vertex = numpy.asarray(ls._vertex * M[:3,:3]) + matrix[:3,3]
lineset.py: self._normal = numpy.asarray(ls._normal * M[:3,:3])
polylist.py: self._vertex = None if pl._vertex is None else numpy.asarray(pl._vertex * M[:3,:3]) + matrix[:3,3]
polylist.py: self._normal = None if pl._normal is None else numpy.asarray(pl._normal * M[:3,:3])
triangleset.py: self._vertex = None if ts.vertex is None else numpy.asarray(ts._vertex * M[:3,:3]) + matrix[:3,3]
triangleset.py: self._normal = None if ts._normal is None else numpy.asarray(ts._normal * M[:3,:3])
PyCollada Primitive Fix
~~~~~~~~~~~~~~~~~~~~~~~~~~
Testing primfix in daegeom.py shows that switching to using non-transposed rotation matrix and
keeping post-multiplication achieves a match for PV1
::
simon:~ blyth$ daegeom.py $LOCAL_BASE/env/graphics/collada/0.dae 1
INFO:env.graphics.collada.pycollada.daegeom:dump_geom from /usr/local/env/graphics/collada/0.dae boundgeom index 1
before primfix nvtx: 8
[[ -23931.1484375 -767540.125 22890. ]
[ -51089.8515625 -809521.125 22890. ]
[ -9108.85058594 -836679.875 22890. ]
[ 18049.8515625 -794698.875 22890. ]
[ 18049.8515625 -794698.875 -15103.79980469]
[ -23931.1484375 -767540.125 -15103.79980469]
[ -9108.85058594 -836679.875 -15103.79980469]
[ -51089.8515625 -809521.125 -15103.79980469]]
after primfix nvtx: 8
[[ 18049.8515625 -809521.125 22890. ]
[ -9108.85058594 -767540.125 22890. ]
[ -51089.8515625 -794698.875 22890. ]
[ -23931.1484375 -836679.875 22890. ]
[ -23931.1484375 -836679.875 -15103.79980469]
[ 18049.8515625 -809521.125 -15103.79980469]
[ -51089.8515625 -794698.875 -15103.79980469]
[ -9108.85058594 -767540.125 -15103.79980469]]
from VRML2DB:
[[ 18049.90039062 -809521. 22890. ]
[ -9108.86035156 -767540. 22890. ]
[ -51089.8984375 -794699. 22890. ]
[ -23931.09960938 -836680. 22890. ]
[ -23931.09960938 -836680. -15104.20019531]
[ 18049.90039062 -809521. -15104.20019531]
[ -51089.8984375 -794699. -15104.20019531]
[ -9108.86035156 -767540. -15104.20019531]]
::
sqlite> select sid,id,x,y,z from dae.point where sid=1 ;
sid id x y z
---------- ---------- -------------- ----------- ----------
1 0 -23931.1484375 -767540.125 22890.0
1 1 -51089.8515625 -809521.125 22890.0
1 2 -9108.85058593 -836679.875 22890.0
1 3 18049.8515625 -794698.875 22890.0
1 4 18049.8515625 -794698.875 -15104.200
1 5 -23931.1484375 -767540.125 -15104.200
1 6 -9108.85058593 -836679.875 -15104.200
1 7 -51089.8515625 -809521.125 -15104.200
G4DAEWriteStructure::MatrixWrite
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
42 void G4DAEWriteStructure::MatrixWrite(xercesc::DOMElement* nodeElement, const G4Transform3D& T)
43 {
44 std::ostringstream ss ;
45 // row-major order
46
47 ss << "\n\t\t\t\t" ;
48 ss << T.xx() << " " ;
49 ss << T.xy() << " " ;
50 ss << T.xz() << " " ;
51 ss << T.dx() << "\n" ;
52
53 ss << T.yx() << " " ;
54 ss << T.yy() << " " ;
55 ss << T.yz() << " " ;
56 ss << T.dy() << "\n" ;
57
58 ss << T.zx() << " " ;
59 ss << T.zy() << " " ;
60 ss << T.zz() << " " ;
61 ss << T.dz() << "\n" ;
62
63 ss << "0.0 0.0 0.0 1.0\n" ;
64
65 std::string fourbyfour = ss.str();
66 xercesc::DOMElement* matrixElement = NewTextElement("matrix", fourbyfour);
67 nodeElement->appendChild(matrixElement);
68 }
::
71 void G4DAEWriteStructure::PhysvolWrite(xercesc::DOMElement* parentNodeElement,
72 const G4VPhysicalVolume* const physvol,
73 const G4Transform3D& T,
74 const G4String& ModuleName)
75 {
76 const G4String pvname = GenerateName(physvol->GetName(),physvol);
77 const G4String lvname = GenerateName(physvol->GetLogicalVolume()->GetName(),physvol->GetLogicalVolume() );
78
79 G4int copyNo = physvol->GetCopyNo(); //why always zero ?
80 if(copyNo != 0) G4cout << "G4DAEWriteStructure::PhysvolWrite " << pvname << " " << copyNo << G4endl ;
81
82 xercesc::DOMElement* childNodeElement = NewElementOneNCNameAtt("node","id",pvname);
83 MatrixWrite( childNodeElement, T );
84
85 xercesc::DOMElement* instanceNodeElement = NewElementOneNCNameAtt("instance_node", "url", lvname , true);
86
87 childNodeElement->appendChild(instanceNodeElement);
88 parentNodeElement->appendChild(childNodeElement);
89 }
::
145 G4Transform3D G4DAEWriteStructure::
146 TraverseVolumeTree(const G4LogicalVolume* const volumePtr, const G4int depth)
147 {
148 if (VolumeMap().find(volumePtr) != VolumeMap().end())
149 {
150 return VolumeMap()[volumePtr]; // Volume is already processed
151 }
152
153 G4VSolid* solidPtr = volumePtr->GetSolid();
154 G4Transform3D R,invR;
...
175 const G4int daughterCount = volumePtr->GetNoDaughters();
...
180 for (G4int i=0;iGetDaughter(i);
...
185 G4Transform3D daughterR;
187 daughterR = TraverseVolumeTree(physvol->GetLogicalVolume(),depth+1);
188
189 G4RotationMatrix rot;
190 if (physvol->GetFrameRotation() != 0)
191 {
192 rot = *(physvol->GetFrameRotation());
193 }
194 G4Transform3D P(rot,physvol->GetObjectTranslation());
195 PhysvolWrite(nodeElement,physvol,invR*P*daughterR,ModuleName);
196 }
...
199 structureElement->appendChild(nodeElement);
... // appended after traversing children
203
204 VolumeMap()[volumePtr] = R;
...
210 return R;
211 }
Need to debug thus,
#. looks like `R,invR,daughterR` will all always be identity matrices,
#. makes the `P` transform blissfully PV local, this is kinda what is needed
G4GDMLReadStructure::PhysvolRead rotation inverted ?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#. hmm the PhysvolRead inverts the rotation
::
256 void G4GDMLReadStructure::
257 PhysvolRead(const xercesc::DOMElement* const physvolElement)
258 {
...
318 G4Transform3D transform(GetRotationMatrix(rotation).inverse(),position);
319 transform = transform*G4Scale3D(scale.x(),scale.y(),scale.z());
320
321 G4String pv_name = logvol->GetName() + "_PV";
322 G4PhysicalVolumesPair pair = G4ReflectionFactory::Instance()
323 ->Place(transform,pv_name,logvol,pMotherLogical,false,0,check);
324
325 if (pair.first != 0) { GeneratePhysvolName(name,pair.first); }
326 if (pair.second != 0) { GeneratePhysvolName(name,pair.second); }
G4Transform3D::G4Transform3D
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
source/global/HEPGeometry/include/G4Transform3D.hh::
34 #include
35
36 typedef HepGeom::Transform3D G4Transform3D;
37
38 typedef HepGeom::Rotate3D G4Rotate3D;
39 typedef HepGeom::RotateX3D G4RotateX3D;
40 typedef HepGeom::RotateY3D G4RotateY3D;
41 typedef HepGeom::RotateZ3D G4RotateZ3D;
42
43 typedef HepGeom::Translate3D G4Translate3D;
44 typedef HepGeom::TranslateX3D G4TranslateX3D;
45 typedef HepGeom::TranslateY3D G4TranslateY3D;
46 typedef HepGeom::TranslateZ3D G4TranslateZ3D;
* /data1/env/local/dyb/external/build/LCG/clhep/2.0.4.2/CLHEP/Geometry/Geometry/Transform3D.h
* /data1/env/local/dyb/external/build/LCG/clhep/2.0.4.2/CLHEP/Geometry/Geometry/Transform3D.icc
PyCollada Recursive Transformations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Within a node the matrix is composed from `I*t[0]*t[1]` for G4DAEWrite a single matrix is written only
so no complications here. However moving to decomposed R and T might be beneficial.
::
307 class Node(SceneNode):
308 """Represents a node object, which is a point on the scene graph, as defined in the collada tag.
309
310 Contains the list of transformations effecting the node as well as any children.
311 """
312
313 def __init__(self, id, children=None, transforms=None, xmlnode=None):
...
335 self.transforms = []
336 if transforms is not None:
337 self.transforms = transforms
338 """A list of transformations effecting the node. This can
339 contain any object that inherits from :class:`collada.scene.Transform`"""
340 self.matrix = numpy.identity(4, dtype=numpy.float32)
341 """A numpy.array of size 4x4 containing a transformation matrix that
342 combines all the transformations in :attr:`transforms`. This will only
343 be updated after calling :meth:`save`."""
344
345 for t in self.transforms:
346 self.matrix = numpy.dot(self.matrix, t.matrix)
...
358 def objects(self, tipo, matrix=None):
359 """Iterate through all objects under this node that match `tipo`.
360 The objects will be bound and transformed via the scene transformations.
361
362 :param str tipo:
363 A string for the desired object type. This can be one of 'geometry',
364 'camera', 'light', or 'controller'.
365 :param numpy.matrix matrix:
366 An optional transformation matrix
367
368 :rtype: generator that yields the type specified
369
370 """
371 if matrix != None: M = numpy.dot( matrix, self.matrix )
372 else: M = self.matrix
373 for node in self.children:
374 for obj in node.objects(tipo, M):
375 yield obj
Current recursion level matrix `self.matrix` post-multiplies the the matrix passed from parent,
so where the `matrix` from above is in brackets you end up with::
( PV0 ) * PV1
( PV0 * PV1 ) * PV2
( PV0 * PV1 * PV2 ) * PV3
Geant4 Transform handling
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/data1/env/local/dyb/external/build/LCG/geant4.9.2.p01/source/visualization/VRML/src/G4VRML2SceneHandlerFunc.icc::
413 void G4VRML2SCENEHANDLER::BeginPrimitives(const G4Transform3D& objectTransformation)
414 {
415 G4VSceneHandler::BeginPrimitives (objectTransformation);
416 fpObjectTransformation = &objectTransformation;
417 #if defined DEBUG_SCENE_FUNC
418 G4cerr << "***** BeginPrimitives " << "\n" ;
419 #endif
420 VRMLBeginModeling();
421 }
geant4.9.2.p01/source/visualization/management/src/G4VisManager.cc::
447 void G4VisManager::Draw (const G4Polyhedron& polyhedron,
448 const G4Transform3D& objectTransform) {
449 if (IsValidView ()) {
450 ClearTransientStoreIfMarked();
451 fpSceneHandler -> BeginPrimitives (objectTransform);
452 fpSceneHandler -> AddPrimitive (polyhedron);
453 fpSceneHandler -> EndPrimitives ();
454 }
455 }
Geant4 using recursive post-multiplication in G4PhysicalVolumeModel::DescribeAndDescend::
336 void G4PhysicalVolumeModel::DescribeAndDescend
337 (G4VPhysicalVolume* pVPV,
338 G4int requestedDepth,
339 G4LogicalVolume* pLV,
340 G4VSolid* pSol,
341 G4Material* pMaterial,
342 const G4Transform3D& theAT,
343 G4VGraphicsScene& sceneHandler)
344 {
345 // Maintain useful data members...
346 fpCurrentPV = pVPV;
347 fpCurrentLV = pLV;
348 fpCurrentMaterial = pMaterial;
349
350 const G4RotationMatrix objectRotation = pVPV -> GetObjectRotationValue ();
351 const G4ThreeVector& translation = pVPV -> GetTranslation ();
352 G4Transform3D theLT (G4Transform3D (objectRotation, translation));
353
354 // Compute the accumulated transformation...
355 // Note that top volume's transformation relative to the world
356 // coordinate system is specified in theAT == startingTransformation
357 // = fTransform (see DescribeYourselfTo), so first time through the
358 // volume's own transformation, which is only relative to its
359 // mother, i.e., not relative to the world coordinate system, should
360 // not be accumulated.
361 G4Transform3D theNewAT (theAT);
362 if (fCurrentDepth != 0) theNewAT = theAT * theLT;
363 fpCurrentTransform = &theNewAT;
364