FreeCAD-macros

Форк
0
/
LasercutterTechdrawExport.py 
260 строк · 9.9 Кб
1
# -*- coding: utf-8 -*-
2

3
# compatible to Python 3
4

5
import os
6
import FreeCAD as app
7
import FreeCADGui as gui
8
from FreeCAD import Vector, Rotation
9
import Part
10
import math
11

12

13
iconPath = os.path.dirname(__file__)
14

15
epsilon = 1e-7      
16
        
17
class LasercutterTechdrawExportItem:
18
    def __init__(self, 
19
                 fp,    # an instance of Part::FeaturePython
20
                 Part = None,
21
                 BeamWidth = 0.2,
22
                 Normal = Vector(0, 0, 0),
23
                 method = 'auto'):
24
        self.updating = False
25
        fp.addProperty('App::PropertyLink', 'Part',  'LasercutterTechdrawExport',  'Selected part').Part = Part
26
        fp.addProperty('App::PropertyVector', 'Normal', 'LasercutterTechdrawExport',  'vertical vector. (0, 0, 0) = rotate the part that it fits best').Normal = Normal
27
        fp.addProperty('App::PropertyFloat', 'BeamWidth', 'LasercutterTechdrawExport',  'Laser beam width in mm').BeamWidth = BeamWidth
28
        fp.addProperty('App::PropertyEnumeration', 'Method', 'LasercutterTechdrawExport',  'How to create the outline').Method = ['auto', '2D', '3D', 'face', 'normal']
29
        fp.Method = method
30
        fp.Proxy = self
31
    
32
    def execute(self, fp):
33
        '''Do something when doing a recomputation, this method is mandatory'''
34
        if fp.Part and fp.Normal and (not self.updating):
35
            self.make_outline(fp)
36
        
37
    def onChanged(self, fp, prop):
38
        '''Do something when a property has changed'''
39
        props = ['Part', 'BeamWidth', 'Normal', 'Method']
40
        if prop in props:
41
            self.execute(fp)         
42
            
43
    def make_outline(self, fp): 
44
        self.updating = True 
45
        
46
        if fp.Method == 'normal': 
47
            outline = fp.Part.Shape.makeOffsetShape(fp.BeamWidth / 2, 1e-7)
48
        elif fp.Method == '2D':
49
            outline = fp.Part.Shape.makeOffset2D(fp.BeamWidth / 2) 
50
            fp.Normal = self.getNormal(fp.Part) 
51
        elif fp.Method == '3D':
52
            outline = fp.Part.Shape.makeOffsetShape(fp.BeamWidth / 2, 1e-7)
53
            fp.Normal = self.getNormal(fp.Part) 
54
        else:
55
            face = self.get_biggest_face(fp.Part)
56
            if face:
57
                outline = face.makeOffset2D(fp.BeamWidth / 2)
58
                fp.Normal = face.normalAt(0, 0)
59
            elif fp.Method == 'auto':
60
                try:
61
                    outline = fp.Part.Shape.makeOffset2D(fp.BeamWidth / 2) 
62
                except Exception as ex:
63
                    outline = fp.Part.Shape.makeOffsetShape(fp.BeamWidth / 2, 1e-7)   
64
                    
65
                fp.Normal = self.getNormal(fp.Part)   
66
            
67
        fp.Shape = Part.Compound(outline.Wires);
68
        fp.Label = fp.Part.Label + ' offset'
69
        fp.Placement = outline.Placement
70
        
71
        if fp.Placement.Rotation.Axis.z < 0:
72
            fp.Placement.Rotation.Axis = fp.Placement.Rotation.Axis * -1
73
        
74
        if fp.Method != 'normal':      
75
            if fp.Normal.z < 0:
76
                fp.Normal = fp.Normal * -1
77
                
78
            rotation_to_apply = Rotation(fp.Normal, Vector(0, 0, 1))    
79
            new_rotation = rotation_to_apply.multiply(fp.Placement.Rotation)
80
            fp.Placement.Rotation = new_rotation
81
        
82
            self.rotate_biggest_side_up(fp)
83
            
84
        self.updating = False
85
        
86
    def get_biggest_face(self, part):
87
        max_area = 0
88
        max_face = None
89
        for face in part.Shape.Faces:
90
            if face and face.Area > max_area:
91
                max_area = face.Area
92
                max_face = face
93
    
94
        if max_face:
95
            return max_face
96
        
97
    def rotate_biggest_side_up(self, fp):
98
        bbox = fp.Shape.optimalBoundingBox()
99
        xmin = bbox.XLength
100
        angle = 0.0
101
        r = fp.Placement.Rotation
102
        r_best = r
103
        step = 180 / 16
104
        while angle + step < 180:   
105
            angle = angle + step 
106
            rotation_to_apply = Rotation()
107
            rotation_to_apply.Axis = Vector(0, 0, 1)             
108
            rotation_to_apply.Angle = math.radians(angle)              
109
            fp.Placement.Rotation = rotation_to_apply.multiply(r)
110
            bbox = fp.Shape.optimalBoundingBox()
111
    
112
            if xmin > bbox.XLength:
113
                xmin = bbox.XLength
114
                r_best = fp.Placement.Rotation
115
                 
116
        fp.Placement.Rotation = r_best
117
        
118
    def getNormal(self, obj):
119
        if hasattr(obj, 'Dir'):
120
            return obj.Dir
121
        else:
122
            bbox = obj.Shape.BoundBox
123
            if bbox.XLength < epsilon: return Vector(1.0,0.0,0.0)
124
            elif bbox.YLength < epsilon: return Vector(0.0,1.0,0.0)
125
            elif bbox.ZLength < epsilon: return Vector(0.0,0.0,1.0)
126
            return obj.Placement.Rotation.multVec(Vector(0, 0, 1))
127

128

129
class LasercutterTechdrawExportItemViewProvider:
130
    def __init__(self, vobj):
131
        '''Set this object to the proxy object of the actual view provider'''
132
        vobj.Proxy = self
133
        self.Object = vobj.Object
134
            
135
    def getIcon(self):
136
        '''Return the icon which will FreeCADear in the tree view. This method is optional and if not defined a default icon is shown.'''
137
        return (os.path.join(iconPath, 'LasercutterTechdrawExport.svg'))
138

139
    def attach(self, vobj):
140
        '''Setup the scene sub-graph of the view provider, this method is mandatory'''
141
        self.Object = vobj.Object
142
        self.onChanged(vobj, 'Base')
143
 
144
    def updateData(self, fp, prop):
145
        '''If a property of the handled feature has changed we have the chance to handle this here'''
146
        pass
147
    
148
    def claimChildren(self):
149
        '''Return a list of objects that will be modified by this feature'''
150
        pass
151
        
152
    def onDelete(self, feature, subelements):
153
        '''Here we can do something when the feature will be deleted'''
154
        return True
155
    
156
    def onChanged(self, fp, prop):
157
        '''Here we can do something when a single property got changed'''
158
        pass
159
        
160
    def setEdit(self, vobj=None, mode=0):
161
        return False
162
        
163
    def __getstate__(self):
164
        '''When saving the document this object gets stored using Python's json module.\
165
                Since we have some un-serializable parts here -- the Coin stuff -- we must define this method\
166
                to return a tuple of all serializable objects or None.'''
167
        return None
168
 
169
    def __setstate__(self,state):
170
        '''When restoring the serialized object from document we have the chance to set some internals here.\
171
                Since no data were serialized nothing needs to be done here.'''
172
        return None
173
        
174

175

176
def selected_to_techdraw(doc, offsets, techdraw, BeamWidth):
177
    x = BeamWidth
178
    y = 0
179
    
180
    for offset in offsets:
181
        viewname = offset.Label.replace('offset', 'contour')
182
        views = doc.getObjectsByLabel(viewname)
183
        if len(views) > 0:
184
            view = views[0]
185
        else:
186
            view = doc.addObject('TechDraw::DrawViewPart', viewname)
187
            techdraw.addView(view)
188
                   
189
        try:
190
            view.CoarseView = False
191
            view.ViewObject.LineWidth = BeamWidth
192
            view.Source = offset
193
            view.Direction = Vector(0, 0, 1)
194
            view.ScaleType = u'Custom'
195
            view.Scale = 1.00
196
        except Exception as ex:
197
            app.Console.PrintError('\nview for ' + viewname + ' cannot be created ! ')
198
            app.Console.PrintError(ex)
199
    
200
    for view in techdraw.Views:
201
        offset = view.Source[0]
202
        bbox = offset.Shape.BoundBox
203
        bsize = Vector(bbox.XLength, bbox.YLength, bbox.ZLength)
204
        
205
        # add a 2D view to the TechDraw page right of the last part
206
        maxheight = y + bsize.y + BeamWidth
207
        if maxheight > techdraw.Template.Height:
208
            techdraw.Template.Height = maxheight
209

210
        maxwidth = x + bsize.x + BeamWidth
211
        if maxwidth > techdraw.Template.Width:
212
            techdraw.Template.Width = maxwidth
213
                          
214
        view.X = x + bsize.x / 2
215
        view.Y = y + bsize.y - (bsize.y / 2)
216
        x = x + bsize.x + BeamWidth
217

218
def makeLasercutterTechdrawExport(parts, BeamWidth = 0.2, doc = app.activeDocument(), method = 'auto', normal = Vector(0, 0, 0)):
219
    if len(parts) == 0: return
220
             
221
    techdraw = doc.addObject('TechDraw::DrawPage','LasercutterTechdraw')
222
    template = doc.addObject('TechDraw::DrawSVGTemplate','Template')
223
    techdraw.Template = template
224
    doc.recompute()
225
    
226
    for p in parts:
227
        if len(p.Shape.Solids) > 1:
228
            for sol in p.Shape.Solids:
229
                sfp = doc.addObject('Part::Feature', p.Label) 
230
                sfp.Shape = Part.Shape(sol)
231
                sfp.ViewObject.hide()
232
                addToExportObjects(doc, sfp)
233
                addLasercutterTechdrawItem(techdraw, sfp, BeamWidth, doc, method, normal)
234
                
235
        else:
236
            addLasercutterTechdrawItem(techdraw, p, BeamWidth, doc, method, normal)
237
        
238
    doc.recompute() 
239
    techdraw.ViewObject.show() 
240
    return techdraw
241
    
242
    
243
def addLasercutterTechdrawItem(techdraw, part, BeamWidth = 0.2, doc = app.activeDocument(), method = 'auto', normal = Vector(0, 0, 0)):       
244
    ifp = doc.addObject('Part::FeaturePython', 'LasercutterTechdrawExport')
245
    LasercutterTechdrawExportItem(ifp, part, BeamWidth, method=method, Normal=normal)
246
    LasercutterTechdrawExportItemViewProvider(ifp.ViewObject)
247
    doc.recompute()
248
    selected_to_techdraw(doc, [ifp], techdraw, BeamWidth)
249
    addToExportObjects(doc, ifp)
250
    return ifp
251
    
252
def addToExportObjects(doc, ifp):
253
    LaserCutterExportObjects = doc.getObjectsByLabel('LaserCutterExportObjects')
254
    if len(LaserCutterExportObjects) == 0:
255
        LaserCutterExportObjects = doc.addObject('App::DocumentObjectGroup', 'LaserCutterExportObjects')
256
    else:
257
        LaserCutterExportObjects = LaserCutterExportObjects[0]
258
        
259
    LaserCutterExportObjects.Group = LaserCutterExportObjects.Group + [ifp]
260
    LaserCutterExportObjects.ViewObject.hide()
261

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

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

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

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