FreeCAD

Форк
0
/
make_sketch.py 
350 строк · 14.3 Кб
1
# ***************************************************************************
2
# *   Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net>        *
3
# *   Copyright (c) 2009, 2010 Ken Cline <cline@frii.com>                   *
4
# *   Copyright (c) 2020 FreeCAD Developers                                 *
5
# *                                                                         *
6
# *   This program is free software; you can redistribute it and/or modify  *
7
# *   it under the terms of the GNU Lesser General Public License (LGPL)    *
8
# *   as published by the Free Software Foundation; either version 2 of     *
9
# *   the License, or (at your option) any later version.                   *
10
# *   for detail see the LICENCE text file.                                 *
11
# *                                                                         *
12
# *   This program is distributed in the hope that it will be useful,       *
13
# *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
# *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
# *   GNU Library General Public License for more details.                  *
16
# *                                                                         *
17
# *   You should have received a copy of the GNU Library General Public     *
18
# *   License along with this program; if not, write to the Free Software   *
19
# *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
20
# *   USA                                                                   *
21
# *                                                                         *
22
# ***************************************************************************
23
"""Provides functions to create Sketch objects from Draft objects."""
24
## @package make_sketch
25
# \ingroup draftmake
26
# \brief Provides functions to create Sketch objects from Draft objects.
27

28
## \addtogroup draftmake
29
# @{
30
import math
31

32
import FreeCAD as App
33
import DraftVecUtils
34
import DraftGeomUtils
35
import draftutils.utils as utils
36
import draftutils.gui_utils as gui_utils
37

38
from draftutils.translate import translate
39

40

41
def make_sketch(objects_list, autoconstraints=False, addTo=None,
42
                delete=False, name="Sketch", radiusPrecision=-1, tol=1e-3):
43
    """make_sketch(objects_list, [autoconstraints], [addTo], [delete],
44
                   [name], [radiusPrecision], [tol])
45

46
    Makes a Sketch objects_list with the given Draft objects.
47

48
    Parameters
49
    ----------
50
    objects_list: can be single or list of objects of Draft type objects,
51
        Part::Feature, Part.Shape, or mix of them.
52

53
    autoconstraints(False): if True, constraints will be automatically added to
54
        wire nodes, rectangles and circles.
55

56
    addTo(None) : if set to an existing sketch, geometry will be added to it
57
        instead of creating a new one.
58

59
    delete(False): if True, the original object will be deleted.
60
        If set to a string 'all' the object and all its linked object will be
61
        deleted.
62

63
    name('Sketch'): the name for the new sketch object.
64

65
    radiusPrecision(-1): If <0, disable radius constraint. If =0, add individual
66
        radius constraint. If >0, the radius will be rounded according to this
67
        precision, and 'Equal' constraint will be added to curve with equal
68
        radius within precision.
69

70
    tol(1e-3): Tolerance used to check if the shapes are planar and coplanar.
71
        Consider change to tol=-1 for a more accurate analysis.
72
    """
73

74
    if not App.ActiveDocument:
75
        App.Console.PrintError("No active document. Aborting\n")
76
        return
77

78
    import Part
79
    from Sketcher import Constraint
80
    import Sketcher
81

82
    start_point = 1
83
    end_point = 2
84
    middle_point = 3
85

86
    if App.GuiUp:
87
        v_dir = gui_utils.get_3d_view().getViewDirection()
88
    else:
89
        v_dir = App.Base.Vector(0,0,-1)
90

91
    # lists to accumulate shapes with defined normal and undefined normal
92
    shape_norm_yes = list()
93
    shape_norm_no = list()
94

95
    if not isinstance(objects_list,(list,tuple)):
96
        objects_list = [objects_list]
97

98
    for obj in objects_list:
99
        if isinstance(obj,Part.Shape):
100
            shape = obj
101
        elif not hasattr(obj,'Shape'):
102
            App.Console.PrintError(translate("draft",
103
                                   "No shape found")+"\n")
104
            return None
105
        else:
106
            shape = obj.Shape
107

108
        if not DraftGeomUtils.is_planar(shape, tol):
109
            App.Console.PrintError(translate("draft",
110
                                   "All Shapes must be planar")+"\n")
111
            return None
112

113
        if DraftGeomUtils.get_normal(shape, tol):
114
            shape_norm_yes.append(shape)
115
        else:
116
            shape_norm_no.append(shape)
117

118

119
    shapes_list = shape_norm_yes + shape_norm_no
120

121
    # test if all shapes are coplanar
122
    if len(shape_norm_yes) >= 1:
123
        for shape in shapes_list[1:]:
124
            if not DraftGeomUtils.are_coplanar(shapes_list[0], shape, tol):
125
                App.Console.PrintError(translate("draft",
126
                                       "All Shapes must be coplanar")+"\n")
127
                return None
128
        # define sketch normal
129
        normal = DraftGeomUtils.get_normal(shapes_list[0], tol)
130

131
    else:
132
        # suppose all geometries are straight lines or points
133
        points = [vertex.Point for shape in shapes_list for vertex in shape.Vertexes]
134
        if len(points) >= 2:
135
            poly = Part.makePolygon(points)
136
            if not DraftGeomUtils.is_planar(poly, tol):
137
                App.Console.PrintError(translate("draft",
138
                                       "All Shapes must be coplanar")+"\n")
139
                return None
140
            normal = DraftGeomUtils.get_normal(poly, tol)
141
            if not normal:
142
                # all points aligned
143
                poly_dir = poly.Edges[0].Curve.Direction
144
                normal = (v_dir - v_dir.dot(poly_dir)*poly_dir).normalize()
145
                normal = normal.negative()
146
        else:
147
            # only one point
148
            normal = v_dir.negative()
149

150

151
    if addTo:
152
        nobj = addTo
153
    else:
154
        nobj = App.ActiveDocument.addObject("Sketcher::SketchObject", name)
155

156
    # Collect constraints and add in one go to improve performance
157
    constraints = []
158
    radiuses = {}
159

160
    def addRadiusConstraint(edge):
161
        try:
162
            if radiusPrecision<0:
163
                return
164
            if radiusPrecision==0:
165
                constraints.append(Constraint('Radius',
166
                        nobj.GeometryCount-1, edge.Curve.Radius))
167
                return
168
            r = round(edge.Curve.Radius,radiusPrecision)
169
            constraints.append(Constraint('Equal',
170
                    radiuses[r],nobj.GeometryCount-1))
171
        except KeyError:
172
            radiuses[r] = nobj.GeometryCount-1
173
            constraints.append(Constraint('Radius',nobj.GeometryCount-1, r))
174
        except AttributeError:
175
            pass
176

177
    def convertBezier(edge):
178
        if DraftGeomUtils.geomType(edge) == "BezierCurve":
179
            return(edge.Curve.toBSpline(edge.FirstParameter,edge.LastParameter).toShape())
180
        else:
181
            return edge
182

183

184
    axis = App.Vector(0, 0, 1).cross(normal)
185
    angle = DraftVecUtils.angle(normal, App.Vector(0, 0, 1)) * App.Units.Radian
186
    rotation = App.Rotation(axis, angle)
187

188
    point = shapes_list[0].Vertexes[0].Point
189
    base = App.Vector(normal)
190
    base.Length = point.dot(base.normalize()) # See https://forum.freecad.org/viewtopic.php?f=22&t=69304#p601149
191

192
    nobj.Placement = App.Placement(base, rotation)
193

194
    for obj in objects_list:
195
        ok = False
196
        tp = utils.get_type(obj)
197

198
        if tp in ["Circle","Ellipse"]:
199
            if obj.Shape.Edges:
200
                edge = obj.Shape.Edges[0]
201
                if len(edge.Vertexes) == 1:
202
                    newedge = DraftGeomUtils.orientEdge(edge, normal)
203
                    nobj.addGeometry(newedge)
204
                else:
205
                    # make new ArcOfCircle
206
                    circle = DraftGeomUtils.orientEdge(edge, normal)
207
                    first  = math.radians(obj.FirstAngle)
208
                    last   = math.radians(obj.LastAngle)
209
                    arc    = Part.ArcOfCircle(circle, first, last)
210
                    nobj.addGeometry(arc)
211
                addRadiusConstraint(edge)
212
                ok = True
213

214
        elif tp in ["Wire", "Rectangle", "Polygon"] and obj.FilletRadius.Value == 0:
215
            if obj.Shape.Edges:
216
                for edge in obj.Shape.Edges:
217
                    nobj.addGeometry(DraftGeomUtils.orientEdge(edge, normal))
218
                if autoconstraints:
219
                    closed = tp in ["Rectangle", "Polygon"] or obj.Closed
220
                    last = nobj.GeometryCount
221
                    segs = list(range(last - len(obj.Shape.Edges), last))
222
                    nexts = segs[1:] + ([segs[0]] if closed else [None])
223
                    for seg, next in zip(segs, nexts):
224
                        if next is not None:
225
                            constraints.append(Constraint("Coincident",seg, end_point, next, start_point))
226
                        if DraftGeomUtils.isAligned(nobj.Geometry[seg], "x"):
227
                            constraints.append(Constraint("Vertical", seg))
228
                        elif DraftGeomUtils.isAligned(nobj.Geometry[seg], "y"):
229
                            constraints.append(Constraint("Horizontal", seg))
230
                ok = True
231

232
        elif tp == "BSpline":
233
            if obj.Shape.Edges:
234
                edge = DraftGeomUtils.orientEdge(obj.Shape.Edges[0], normal)
235
                nobj.addGeometry(edge)
236
                nobj.exposeInternalGeometry(nobj.GeometryCount-1)
237
                ok = True
238

239
        elif tp == "BezCurve":
240
            if obj.Shape.Edges:
241
                for piece in obj.Shape.Edges:
242
                    bez = piece.Curve
243
                    bsp = bez.toBSpline(bez.FirstParameter,bez.LastParameter).toShape()
244
                    edge = DraftGeomUtils.orientEdge(bsp.Edges[0], normal)
245
                    nobj.addGeometry(edge)
246
                    nobj.exposeInternalGeometry(nobj.GeometryCount-1)
247
                ok = True
248
                # TODO: set coincident constraint for vertexes in multi-edge bezier curve
249

250
        elif  tp == "Point":
251
            shape = obj.Shape.copy()
252
            if angle:
253
                shape.rotate(App.Base.Vector(0,0,0), axis, -1*angle)
254
            point = Part.Point(shape.Point)
255
            nobj.addGeometry(point)
256
            ok = True
257

258
        elif tp == 'Shape' or hasattr(obj,'Shape'):
259
            shape = obj if tp == 'Shape' else obj.Shape
260
            if not shape.Wires:
261
                for e in shape.Edges:
262
                    # unconnected edges
263
                    newedge = convertBezier(e)
264
                    nobj.addGeometry(DraftGeomUtils.orientEdge(
265
                                        newedge, normal, make_arc=True))
266
                    addRadiusConstraint(newedge)
267

268
            if autoconstraints:
269
                for wire in shape.Wires:
270
                    last_count = nobj.GeometryCount
271
                    edges = wire.OrderedEdges
272
                    for edge in edges:
273
                        newedge = convertBezier(edge)
274
                        nobj.addGeometry(DraftGeomUtils.orientEdge(
275
                                            newedge, normal, make_arc=True))
276
                        addRadiusConstraint(newedge)
277
                    for i,g in enumerate(nobj.Geometry[last_count:]):
278
                        if edges[i].Closed:
279
                            continue
280
                        seg = last_count+i
281

282
                        if DraftGeomUtils.isAligned(g,"x"):
283
                            constraints.append(Constraint("Vertical",seg))
284
                        elif DraftGeomUtils.isAligned(g,"y"):
285
                            constraints.append(Constraint("Horizontal",seg))
286

287
                        if seg == nobj.GeometryCount-1:
288
                            if not wire.isClosed():
289
                                break
290
                            g2 = nobj.Geometry[last_count]
291
                            seg2 = last_count
292
                        else:
293
                            seg2 = seg+1
294
                            g2 = nobj.Geometry[seg2]
295

296
                        end1 = g.value(g.LastParameter)
297
                        start2 = g2.value(g2.FirstParameter)
298
                        if DraftVecUtils.equals(end1,start2) :
299
                            constraints.append(Constraint(
300
                                "Coincident",seg,end_point,seg2,start_point))
301
                            continue
302
                        end2 = g2.value(g2.LastParameter)
303
                        start1 = g.value(g.FirstParameter)
304
                        if DraftVecUtils.equals(end2,start1):
305
                            constraints.append(Constraint(
306
                                "Coincident",seg,start_point,seg2,end_point))
307
                        elif DraftVecUtils.equals(start1,start2):
308
                            constraints.append(Constraint(
309
                                "Coincident",seg,start_point,seg2,start_point))
310
                        elif DraftVecUtils.equals(end1,end2):
311
                            constraints.append(Constraint(
312
                                "Coincident",seg,end_point,seg2,end_point))
313
            else:
314
                for wire in shape.Wires:
315
                    for edge in wire.OrderedEdges:
316
                        newedge = convertBezier(edge)
317
                        nobj.addGeometry(DraftGeomUtils.orientEdge(
318
                                            newedge, normal, make_arc=True))
319
            ok = True
320
        gui_utils.format_object(nobj,obj)
321
        if ok and delete and hasattr(obj,'Shape'):
322
            doc = obj.Document
323
            def delObj(obj):
324
                if obj.InList:
325
                    App.Console.PrintWarning(translate("draft",
326
                        "Cannot delete object {} with dependency".format(obj.Label))+"\n")
327
                else:
328
                    doc.removeObject(obj.Name)
329
            try:
330
                if delete == 'all':
331
                    objs = [obj]
332
                    while objs:
333
                        obj = objs[0]
334
                        objs = objs[1:] + obj.OutList
335
                        delObj(obj)
336
                else:
337
                    delObj(obj)
338
            except Exception as ex:
339
                App.Console.PrintWarning(translate("draft",
340
                    "Failed to delete object {}: {}".format(obj.Label,ex))+"\n")
341

342

343
    nobj.addConstraint(constraints)
344

345
    return nobj
346

347

348
makeSketch = make_sketch
349

350
## @}
351

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

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

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

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