FreeCAD / FreeCAD-macros

A repository for FreeCAD macros
https://freecad.org/wiki/Macros_recipes
156 stars 139 forks source link

[GetGlobalPlacement] Issue with the scale computation #159

Open galou opened 1 year ago

galou commented 1 year ago

The macro return the wrong result for an object with rotation 0.45642222410108496, 0.49809724456331667, 0.5849212871934979, -0.4488262203714842. The returned scale factor is -1, -1, 1 but should be 1, 1, 1. According to this, the scale computation involves the eigenvalue decomposition.

I already fixed the issue for objects that are not scaled in 47dbb41. The issue subsists for scaled objects but a warning is printed.

I added a test for this.

@Jolbas do you have the capacity to have a look at this?

galou commented 1 year ago

Maybe a solution, given a Matrix m with rotation and scaling, Rotation(m).toMatrix().inverse() * m returns a matrix whose diagonal elements are the scaling factors. The method is not very accurate though (+/- 0.0003) and I don't know how generic it is.

Jolbas commented 1 year ago

I don't know how to do the polar decomposition yet. But I know for sure there is no way to distinguish between a matrix representing no scaling at all and one that is a combination of scale(-1,-1,1) and a rotation of 180 degrees. Only option is to assume that all scale factors have the same sign. There is work going on to fix related issues in the creation of a rotation from a matrix. Maybe following code will work for now.

    return_type_link_matrix = 6  # Cf. DocumentObjectPyImp.cpp::getSubObject (l.417).
    matrix = object.getSubObject(subobject_fullpath, return_type_link_matrix,
                                 transform=True)
    if matrix is None:
        return
    scale_type = matrix.hasScale(1e-7)
    if scale_type == ScaleType.Other:
        app.Console.PrintWarning('Too complex transformation to find rotation\n')
        return
    if scale_type == app.ScaleType.NoScaling:
        return app.Placement(matrix), app.Vector(1.0, 1.0, 1.0)
    position = matrix.col(3)
    matrix.setCol(3, app.Vector())
    scale_type = matrix.hasScale(1e-5)
    if scale_type == ScaleType.NonUniformRight:
        matrix.transpose()
    scale_vec = App.Vector(*(m.row(i).Length for i in range(3)))
    matrix.scale(*(1 / s for s in scale_vec))
    if scale_type == ScaleType.NonUniformRight:
        matrix.transpose()
    if matrix.determinant() < 0:
        matrix.scale(-1)
        scale_vec *= -1
    matrix.setCol(3, position)
    return app.Placement(matrix), scale_vec