I have generalized the algorithm so that it can cope with the case like this.

import adsk.core, adsk.fusion, traceback
# Coordinate transformation by matrix
# M: 4x4 transformation matrix
# a: 3D vector
def trans(M, a):
ex = [M[0],M[4],M[8]]
ey = [M[1],M[5],M[9]]
ez = [M[2],M[6],M[10]]
oo = [M[3],M[7],M[11]]
b = [0, 0, 0]
for i in range(3):
b[i] = a[0]*ex[i]+a[1]*ey[i]+a[2]*ez[i]+oo[i]
return(b)
# Returns True if two arrays are element-wise equal within a tolerance
def allclose(v1, v2, tol=1e-6):
return( max([abs(a-b) for a,b in zip(v1, v2)]) < tol )
try:
app = adsk.core.Application.get()
ui = app.userInterface
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
rootComp = design.rootComponent
for joint in rootComp.joints:
# Basic information
xyz_from_one_to_joint = joint.geometryOrOriginOne.origin.asArray() # Relative Joint pos
xyz_from_two_to_joint = joint.geometryOrOriginTwo.origin.asArray() # Relative Joint pos
xyz_of_one = joint.occurrenceOne.transform.translation.asArray() # Link origin
xyz_of_two = joint.occurrenceTwo.transform.translation.asArray() # Link origin
M_two = joint.occurrenceTwo.transform.asArray() # Matrix as a 16 element array.
# Compose joint position
case1 = allclose(xyz_from_two_to_joint, xyz_from_one_to_joint)
case2 = allclose(xyz_from_two_to_joint, xyz_of_one)
if case1 or case2:
xyz_of_joint = xyz_from_two_to_joint
else:
xyz_of_joint = trans(M_two, xyz_from_two_to_joint)
# Show result
print('-----', joint.name, '-----')
print('J1', [round(x,3) for x in xyz_from_one_to_joint])
print('J2', [round(x,3) for x in xyz_from_two_to_joint])
print('L1', [round(x,3) for x in xyz_of_one])
print('L2', [round(x,3) for x in xyz_of_two])
print(case1, case2)
print("Joint position ", [round(x, 3) for x in xyz_of_joint])
except:
print('Failed:\n{}'.format(traceback.format_exc()))
I'm still skeptical about the result:
* Can this algorithm cope with all possible cases ?
* Do AUTODESK engineers do such complicated computation just to get joint coordinates ?