From 9eb59414c18641ee7c4325e70d024edb46fbbadf Mon Sep 17 00:00:00 2001 From: carlopav Date: Sun, 26 Apr 2020 13:51:52 +0200 Subject: [PATCH] Draft: split offset from Draft.py . --- src/Mod/Draft/CMakeLists.txt | 1 + src/Mod/Draft/Draft.py | 188 +------------------ src/Mod/Draft/draftfunctions/offset.py | 246 +++++++++++++++++++++++++ 3 files changed, 249 insertions(+), 186 deletions(-) create mode 100644 src/Mod/Draft/draftfunctions/offset.py diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 30c9558311..ea9144d9a4 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -62,6 +62,7 @@ SET(Draft_functions draftfunctions/__init__.py draftfunctions/join.py draftfunctions/move.py + draftfunctions/offset.py draftfunctions/rotate.py draftfunctions/scale.py draftfunctions/split.py diff --git a/src/Mod/Draft/Draft.py b/src/Mod/Draft/Draft.py index 61b71d9927..83dc50cb77 100644 --- a/src/Mod/Draft/Draft.py +++ b/src/Mod/Draft/Draft.py @@ -194,6 +194,8 @@ from draftfunctions.join import join_two_wires as joinTwoWires from draftfunctions.split import split +from draftfunctions.offset import offset + #--------------------------------------------------------------------------- # Draft objects #--------------------------------------------------------------------------- @@ -685,192 +687,6 @@ def rotateEdge(object, edge_index, angle, center, axis): rotateVertex(object, edge_index+1, angle, center, axis) - - -def offset(obj,delta,copy=False,bind=False,sym=False,occ=False): - """offset(object,delta,[copymode],[bind]): offsets the given wire by - applying the given delta Vector to its first vertex. If copymode is - True, another object is created, otherwise the same object gets - offset. If bind is True, and provided the wire is open, the original - and the offset wires will be bound by their endpoints, forming a face - if sym is True, bind must be true too, and the offset is made on both - sides, the total width being the given delta length. If offsetting a - BSpline, the delta must not be a Vector but a list of Vectors, one for - each node of the spline.""" - import Part, DraftGeomUtils - newwire = None - delete = None - - if getType(obj) in ["Sketch","Part"]: - copy = True - print("the offset tool is currently unable to offset a non-Draft object directly - Creating a copy") - - def getRect(p,obj): - """returns length,height,placement""" - pl = obj.Placement.copy() - pl.Base = p[0] - diag = p[2].sub(p[0]) - bb = p[1].sub(p[0]) - bh = p[3].sub(p[0]) - nb = DraftVecUtils.project(diag,bb) - nh = DraftVecUtils.project(diag,bh) - if obj.Length.Value < 0: l = -nb.Length - else: l = nb.Length - if obj.Height.Value < 0: h = -nh.Length - else: h = nh.Length - return l,h,pl - - def getRadius(obj,delta): - """returns a new radius for a regular polygon""" - an = math.pi/obj.FacesNumber - nr = DraftVecUtils.rotate(delta,-an) - nr.multiply(1/math.cos(an)) - nr = obj.Shape.Vertexes[0].Point.add(nr) - nr = nr.sub(obj.Placement.Base) - nr = nr.Length - if obj.DrawMode == "inscribed": - return nr - else: - return nr * math.cos(math.pi/obj.FacesNumber) - - newwire = None - if getType(obj) == "Circle": - pass - elif getType(obj) == "BSpline": - pass - else: - if sym: - d1 = Vector(delta).multiply(0.5) - d2 = d1.negative() - n1 = DraftGeomUtils.offsetWire(obj.Shape,d1) - n2 = DraftGeomUtils.offsetWire(obj.Shape,d2) - else: - if isinstance(delta,float) and (len(obj.Shape.Edges) == 1): - # circle - c = obj.Shape.Edges[0].Curve - nc = Part.Circle(c.Center,c.Axis,delta) - if len(obj.Shape.Vertexes) > 1: - nc = Part.ArcOfCircle(nc,obj.Shape.Edges[0].FirstParameter,obj.Shape.Edges[0].LastParameter) - newwire = Part.Wire(nc.toShape()) - p = [] - else: - newwire = DraftGeomUtils.offsetWire(obj.Shape,delta) - if DraftGeomUtils.hasCurves(newwire) and copy: - p = [] - else: - p = DraftGeomUtils.getVerts(newwire) - if occ: - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape,delta,occ=True) - formatObject(newobj,obj) - if not copy: - delete = obj.Name - elif bind: - if not DraftGeomUtils.isReallyClosed(obj.Shape): - if sym: - s1 = n1 - s2 = n2 - else: - s1 = obj.Shape - s2 = newwire - if s1 and s2: - w1 = s1.Edges - w2 = s2.Edges - w3 = Part.LineSegment(s1.Vertexes[0].Point,s2.Vertexes[0].Point).toShape() - w4 = Part.LineSegment(s1.Vertexes[-1].Point,s2.Vertexes[-1].Point).toShape() - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = Part.Face(Part.Wire(w1+[w3]+w2+[w4])) - else: - print("Draft.offset: Unable to bind wires") - else: - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = Part.Face(obj.Shape.Wires[0]) - if not copy: - delete = obj.Name - elif copy: - newobj = None - if sym: return None - if getType(obj) == "Wire": - if p: - newobj = makeWire(p) - newobj.Closed = obj.Closed - elif newwire: - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = newwire - else: - print("Draft.offset: Unable to duplicate this object") - elif getType(obj) == "Rectangle": - if p: - length,height,plac = getRect(p,obj) - newobj = makeRectangle(length,height,plac) - elif newwire: - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = newwire - else: - print("Draft.offset: Unable to duplicate this object") - elif getType(obj) == "Circle": - pl = obj.Placement - newobj = makeCircle(delta) - newobj.FirstAngle = obj.FirstAngle - newobj.LastAngle = obj.LastAngle - newobj.Placement = pl - elif getType(obj) == "Polygon": - pl = obj.Placement - newobj = makePolygon(obj.FacesNumber) - newobj.Radius = getRadius(obj,delta) - newobj.DrawMode = obj.DrawMode - newobj.Placement = pl - elif getType(obj) == "BSpline": - newobj = makeBSpline(delta) - newobj.Closed = obj.Closed - else: - # try to offset anyway - try: - if p: - newobj = makeWire(p) - newobj.Closed = obj.Shape.isClosed() - except Part.OCCError: - pass - if not(newobj) and newwire: - newobj = FreeCAD.ActiveDocument.addObject("Part::Feature","Offset") - newobj.Shape = newwire - else: - print("Draft.offset: Unable to create an offset") - if newobj: - formatObject(newobj,obj) - else: - newobj = None - if sym: return None - if getType(obj) == "Wire": - if obj.Base or obj.Tool: - FreeCAD.Console.PrintWarning("Warning: object history removed\n") - obj.Base = None - obj.Tool = None - obj.Points = p - elif getType(obj) == "BSpline": - #print(delta) - obj.Points = delta - #print("done") - elif getType(obj) == "Rectangle": - length,height,plac = getRect(p,obj) - obj.Placement = plac - obj.Length = length - obj.Height = height - elif getType(obj) == "Circle": - obj.Radius = delta - elif getType(obj) == "Polygon": - obj.Radius = getRadius(obj,delta) - elif getType(obj) == 'Part': - print("unsupported object") # TODO - newobj = obj - if copy and getParam("selectBaseObjects",False): - select(newobj) - else: - select(obj) - if delete: - FreeCAD.ActiveDocument.removeObject(delete) - return newobj - def draftify(objectslist,makeblock=False,delete=True): """draftify(objectslist,[makeblock],[delete]): turns each object of the given list (objectslist can also be a single object) into a Draft parametric diff --git a/src/Mod/Draft/draftfunctions/offset.py b/src/Mod/Draft/draftfunctions/offset.py new file mode 100644 index 0000000000..0d25805e35 --- /dev/null +++ b/src/Mod/Draft/draftfunctions/offset.py @@ -0,0 +1,246 @@ +# *************************************************************************** +# * Copyright (c) 2009, 2010 Yorik van Havre * +# * Copyright (c) 2009, 2010 Ken Cline * +# * Copyright (c) 2020 FreeCAD Developers * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""This module provides the code for Draft offset function. +""" +## @package offset +# \ingroup DRAFT +# \brief This module provides the code for Draft offset function. + +import math + +import FreeCAD as App + +import DraftVecUtils + +import draftutils.gui_utils as gui_utils +import draftutils.utils as utils + +from draftmake.make_copy import make_copy + +from draftmake.make_rectangle import makeRectangle +from draftmake.make_wire import makeWire +from draftmake.make_polygon import makePolygon +from draftmake.make_circle import makeCircle +from draftmake.make_bspline import makeBSpline + + +def offset(obj, delta, copy=False, bind=False, sym=False, occ=False): + """offset(object,delta,[copymode],[bind]) + + Offset the given wire by applying the given delta Vector to its first + vertex. + + Parameters + ---------- + obj : + + delta : Base.Vector or list of Base.Vector + If offsetting a BSpline, the delta must not be a Vector but a list + of Vectors, one for each node of the spline. + + copy : bool + If copymode is True, another object is created, otherwise the same + object gets offsetted. + + copy : bool + If bind is True, and provided the wire is open, the original + and the offset wires will be bound by their endpoints, forming a face. + + sym : bool + if sym is True, bind must be true too, and the offset is made on both + sides, the total width being the given delta length. + """ + import Part + import DraftGeomUtils + newwire = None + delete = None + + if utils.get_type(obj) in ["Sketch","Part"]: + copy = True + print("the offset tool is currently unable to offset a non-Draft object directly - Creating a copy") + + def getRect(p,obj): + """returns length,height,placement""" + pl = obj.Placement.copy() + pl.Base = p[0] + diag = p[2].sub(p[0]) + bb = p[1].sub(p[0]) + bh = p[3].sub(p[0]) + nb = DraftVecUtils.project(diag,bb) + nh = DraftVecUtils.project(diag,bh) + if obj.Length.Value < 0: l = -nb.Length + else: l = nb.Length + if obj.Height.Value < 0: h = -nh.Length + else: h = nh.Length + return l,h,pl + + def getRadius(obj,delta): + """returns a new radius for a regular polygon""" + an = math.pi/obj.FacesNumber + nr = DraftVecUtils.rotate(delta,-an) + nr.multiply(1/math.cos(an)) + nr = obj.Shape.Vertexes[0].Point.add(nr) + nr = nr.sub(obj.Placement.Base) + nr = nr.Length + if obj.DrawMode == "inscribed": + return nr + else: + return nr * math.cos(math.pi/obj.FacesNumber) + + newwire = None + if utils.get_type(obj) == "Circle": + pass + elif utils.get_type(obj) == "BSpline": + pass + else: + if sym: + d1 = App.Vector(delta).multiply(0.5) + d2 = d1.negative() + n1 = DraftGeomUtils.offsetWire(obj.Shape,d1) + n2 = DraftGeomUtils.offsetWire(obj.Shape,d2) + else: + if isinstance(delta,float) and (len(obj.Shape.Edges) == 1): + # circle + c = obj.Shape.Edges[0].Curve + nc = Part.Circle(c.Center,c.Axis,delta) + if len(obj.Shape.Vertexes) > 1: + nc = Part.ArcOfCircle(nc,obj.Shape.Edges[0].FirstParameter,obj.Shape.Edges[0].LastParameter) + newwire = Part.Wire(nc.toShape()) + p = [] + else: + newwire = DraftGeomUtils.offsetWire(obj.Shape,delta) + if DraftGeomUtils.hasCurves(newwire) and copy: + p = [] + else: + p = DraftGeomUtils.getVerts(newwire) + if occ: + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = DraftGeomUtils.offsetWire(obj.Shape,delta,occ=True) + gui_utils.formatObject(newobj,obj) + if not copy: + delete = obj.Name + elif bind: + if not DraftGeomUtils.isReallyClosed(obj.Shape): + if sym: + s1 = n1 + s2 = n2 + else: + s1 = obj.Shape + s2 = newwire + if s1 and s2: + w1 = s1.Edges + w2 = s2.Edges + w3 = Part.LineSegment(s1.Vertexes[0].Point,s2.Vertexes[0].Point).toShape() + w4 = Part.LineSegment(s1.Vertexes[-1].Point,s2.Vertexes[-1].Point).toShape() + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = Part.Face(Part.Wire(w1+[w3]+w2+[w4])) + else: + print("Draft.offset: Unable to bind wires") + else: + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = Part.Face(obj.Shape.Wires[0]) + if not copy: + delete = obj.Name + elif copy: + newobj = None + if sym: return None + if utils.get_type(obj) == "Wire": + if p: + newobj = makeWire(p) + newobj.Closed = obj.Closed + elif newwire: + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = newwire + else: + print("Draft.offset: Unable to duplicate this object") + elif utils.get_type(obj) == "Rectangle": + if p: + length,height,plac = getRect(p,obj) + newobj = makeRectangle(length,height,plac) + elif newwire: + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = newwire + else: + print("Draft.offset: Unable to duplicate this object") + elif utils.get_type(obj) == "Circle": + pl = obj.Placement + newobj = makeCircle(delta) + newobj.FirstAngle = obj.FirstAngle + newobj.LastAngle = obj.LastAngle + newobj.Placement = pl + elif utils.get_type(obj) == "Polygon": + pl = obj.Placement + newobj = makePolygon(obj.FacesNumber) + newobj.Radius = getRadius(obj,delta) + newobj.DrawMode = obj.DrawMode + newobj.Placement = pl + elif utils.get_type(obj) == "BSpline": + newobj = makeBSpline(delta) + newobj.Closed = obj.Closed + else: + # try to offset anyway + try: + if p: + newobj = makeWire(p) + newobj.Closed = obj.Shape.isClosed() + except Part.OCCError: + pass + if not(newobj) and newwire: + newobj = App.ActiveDocument.addObject("Part::Feature","Offset") + newobj.Shape = newwire + else: + print("Draft.offset: Unable to create an offset") + if newobj: + gui_utils.formatObject(newobj,obj) + else: + newobj = None + if sym: return None + if utils.get_type(obj) == "Wire": + if obj.Base or obj.Tool: + App.Console.PrintWarning("Warning: object history removed\n") + obj.Base = None + obj.Tool = None + obj.Points = p + elif utils.get_type(obj) == "BSpline": + #print(delta) + obj.Points = delta + #print("done") + elif utils.get_type(obj) == "Rectangle": + length,height,plac = getRect(p,obj) + obj.Placement = plac + obj.Length = length + obj.Height = height + elif utils.get_type(obj) == "Circle": + obj.Radius = delta + elif utils.get_type(obj) == "Polygon": + obj.Radius = getRadius(obj,delta) + elif utils.get_type(obj) == 'Part': + print("unsupported object") # TODO + newobj = obj + if copy and utils.get_param("selectBaseObjects",False): + gui_utils.select(newobj) + else: + gui_utils.select(obj) + if delete: + App.ActiveDocument.removeObject(delete) + return newobj