FreeCAD-macros

Форк
0
/
GetGlobalPlacement.FCMacro 
186 строк · 7.2 Кб
1
"""Get the global placement of selected objects, respecting links."""
2
# Usage: select some objects or some subelements, run and get the result in the
3
# Report View.
4

5
__Name__ = 'Get Global Placement'
6
__Comment__ = 'Get the global placement, respecting links, link arrays and link scale'
7
__Author__ = 'galou_breizh, Jolbas'
8
__Version__ = '1.1.2'
9
__Date__ = '2023-02-27'
10
__License__ = 'LGPL-2.0-or-later'
11
__Web__ = '' #'http://forum.freecadweb.org/viewtopic.php?f=?&t=????'
12
__Wiki__ = '' #'http://www.freecadweb.org/wiki/Macro_Title_Of_macro'
13
__Icon__ = ''
14
__Help__ = 'Select some objects or some subelements and run'
15
__Status__ = ''
16
__Requires__ = 'FreeCAD >=0.21'
17
__Communication__ = 'https://github.com/FreeCAD/FreeCAD-macros/issues/'
18
__Files__ = 'GetGlobalPlacement.svg'
19

20
from math import copysign, hypot
21
from typing import Set, Tuple
22

23
import FreeCAD as app
24
import FreeCADGui as gui
25

26

27
def strip_subelement(sub_fullpath: str) -> str:
28
    """Return sub_fullpath without the last sub-element.
29

30
    A sub-element is a face, edge or vertex.
31

32
    Parameters
33
    ----------
34
    - subobject_fullpath: SelectionObject.SubElementNames[i], where
35
        SelectionObject is obtained with gui.Selection.getSelectionEx('', 0)
36
        (i.e. not gui.Selection.getSelectionEx()).
37
        Examples:
38
        - 'Face6' if you select the top face of a cube solid made in Part.
39
        - 'Body.Box001.' if you select the tip of a Part->Body->"additive
40
            primitve" in PartDesign.
41
        - 'Body.Box001.Face6' if you select the top face of a Part->Body->
42
            "additive primitve" in PartDesign.
43

44
    """
45
    return sub_fullpath.rpartition('.')[0]
46

47

48
def get_global_placement_and_scale(
49
        object: app.DocumentObject,
50
        subobject_fullpath: str,
51
        ) -> Tuple[app.Placement, app.Vector]:
52
    """Return the global placement and the total scale, respecting links.
53
    Returns the placement and scale the objects content is related to,
54
    which means the properties LinkTransform and Scale is respected if
55
    path points to a link.
56

57
    This is in contrast with ``object.getGlobalPlacement()`` that returns
58
    the placement of the original object, not the linked one.
59

60
    Parameters
61
    ----------
62
    - root_object: SelectionObject.Object, where SelectionObject is obtained
63
        with gui.Selection.getSelectionEx('', 0)
64
        (i.e. not gui.Selection.getSelectionEx()).
65
    - subobject_fullpath: SelectionObject.SubElementNames[i].
66
        Examples:
67
        - 'Face6' if you select the top face of a cube solid made in Part.
68
        - 'Body.Box001.' if you select the tip of a Part->Body->"additive
69
            primitve" in PartDesign.
70
        - 'Body.Box001.Face6' if you select the top face of a Part->Body->
71
            "additive primitve" in PartDesign.
72
    """
73
    return_type_link_matrix = 6  # Cf. DocumentObjectPyImp.cpp::getSubObject (l.417).
74
    matrix = object.getSubObject(subobject_fullpath, return_type_link_matrix,
75
                                 transform=True)
76
    if matrix is None:
77
        return
78
    scale_type = matrix.hasScale(1e-5)
79
    if scale_type == app.ScaleType.NoScaling:
80
        return app.Placement(matrix), app.Vector(1.0, 1.0, 1.0)
81
    if scale_type != app.ScaleType.Uniform:
82
        app.Console.PrintWarning('Non-uniform scaling not supported\n')
83
        return
84
    app.Console.PrintWarning('Uniform scaling may give wrong results, use with care\n')
85
    # Find scale.
86
    # Works only if uniform?
87
    s_gen = (copysign(hypot(*matrix.col(i)), matrix.col(i)[i])
88
             for i in range(3))
89
    scale_vec = app.Vector(*s_gen)
90
    # Workaround for scale affecting rotation
91
    # see https://forum.freecad.org/viewtopic.php?t=75448
92
    # Remove the scale from the rotation.
93
    position = matrix.col(3)
94
    matrix.setCol(3, app.Vector())
95
    matrix.scale(*(1/s for s in scale_vec))
96
    matrix.setCol(3, position)
97
    return app.Placement(matrix), scale_vec
98

99

100
def get_global_placement(
101
        object: app.DocumentObject,
102
        subobject_fullpath: str,
103
        ) -> app.Placement:
104
    """Return the global placement respecting links.
105
    Returns the placement the objects content is related to, which means
106
    the properties LinkTransform is respected if path points to a link.
107

108
    This is in contrast with ``object.getGlobalPlacement()`` that returns
109
    the placement of the original object, not the linked one.
110

111
    Parameters
112
    ----------
113
    - root_object: SelectionObject.Object, where SelectionObject is obtained
114
        with gui.Selection.getSelectionEx('', 0)
115
        (i.e. not gui.Selection.getSelectionEx()).
116
    - subobject_fullpath: SelectionObject.SubElementNames[i].
117
        Examples:
118
        - 'Face6' if you select the top face of a cube solid made in Part.
119
        - 'Body.Box001.' if you select the tip of a Part->Body->"additive
120
            primitve" in PartDesign.
121
        - 'Body.Box001.Face6' if you select the top face of a Part->Body->
122
            "additive primitve" in PartDesign.
123
    """
124
    p_and_s = get_global_placement_and_scale(object, subobject_fullpath)
125
    if p_and_s is None:
126
        return
127
    return p_and_s[0]
128

129

130
def print_placement(object_name: str,
131
                    sub_fullpath: str,
132
                    placement: app.Placement,
133
                    ) -> None:
134
    """Pretty-print a placement in the console."""
135
    dot = '.' if sub_fullpath else ''
136
    if placement:
137
        app.Console.PrintMessage(
138
            f'{object_name}{dot}{sub_fullpath}:'
139
            + (' {b.x:.3f}, {b.y:.3f}, {b.z:.3f};'
140
               ' {q[0]:.4f}, {q[1]:.4f}, {q[2]:.4f}, {q[3]:.4f};'
141
               ' App.Placement(App.Vector({b.x:.3f}, {b.y:.3f}, {b.z:.3f}),'
142
               ' App.Rotation({q[0]:.5f}, {q[1]:.5f}, {q[2]:.5f}, {q[3]:.5f}));'
143
              ).format(
144
                   b=placement.Base,
145
                   q=placement.Rotation.Q)
146
            + ' (rpy: {r[2]:.2f}, {r[1]:.2f}, {r[0]:.2f}) deg'.format(
147
                r=placement.Rotation.getYawPitchRoll())
148
            + '\n')
149
    else:
150
        app.Console.PrintMessage(
151
            f'{object_name}{dot}{sub_fullpath}:'
152
            + 'Couldn\'t find the placement\n')
153

154

155
def get_and_print_selected_placements():
156
    """Get selected objects and print their placement."""
157
    doc = app.activeDocument()
158
    if doc is None:
159
        return
160

161
    selection = gui.Selection.getSelectionEx('', 0)
162
    if not selection:
163
        app.Console.PrintWarning('Nothing selected, nothing to do\n')
164
        return
165

166
    shown_paths: Set[str] = set()
167
    for selection_object in selection:
168
        object_ = selection_object.Object
169
        sub_fullpaths = selection_object.SubElementNames
170
        if not sub_fullpaths:
171
            # An object is selected, not a face, edge, vertex.
172
            placement = get_global_placement(object_, '')
173
            print_placement(object_.Name, '', placement)
174
            continue
175
        for sub_fullpath in sub_fullpaths:
176
            # One or more subelements are selected.
177
            wo_subelement = strip_subelement(sub_fullpath)
178
            path = object_.Name + wo_subelement
179
            if not path in shown_paths:
180
                shown_paths.add(path)
181
                placement = get_global_placement(object_, sub_fullpath)
182
                print_placement(object_.Name, wo_subelement, placement)
183

184

185
if __name__ == '__main__':
186
    get_and_print_selected_placements()
187

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.