diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index f2924a218f..d63addbdb5 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -31,6 +31,7 @@ SET(Draft_import SET (Draft_geoutils draftgeoutils/__init__.py draftgeoutils/general.py + draftgeoutils/edges.py ) SET(Draft_tests diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 6a1d02e9a4..4cec069637 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -98,14 +98,7 @@ from draftgeoutils.general import isValidPath # edge functions ************************************************************* -def findEdge(anEdge, aList): - """findEdge(anEdge,aList): returns True if anEdge is found in aList of edges""" - for e in range(len(aList)): - if str(anEdge.Curve) == str(aList[e].Curve): - if DraftVecUtils.equals(anEdge.Vertexes[0].Point,aList[e].Vertexes[0].Point): - if DraftVecUtils.equals(anEdge.Vertexes[-1].Point,aList[e].Vertexes[-1].Point): - return(e) - return None +from draftgeoutils.edges import findEdge def findIntersection(edge1, edge2, @@ -424,35 +417,7 @@ def pocket2d(shape, offset): return offsetWires -def orientEdge(edge, normal=None, make_arc=False): - """Re-orients 'edge' such that it is in the x-y plane. If 'normal' is passed, this - is used as the basis for the rotation, otherwise the Placement property of 'edge' - is used""" - import DraftVecUtils - # This 'normalizes' the placement to the xy plane - edge = edge.copy() - xyDir = FreeCAD.Vector(0, 0, 1) - base = FreeCAD.Vector(0,0,0) - - if normal: - angle = DraftVecUtils.angle(normal, xyDir)*FreeCAD.Units.Radian - axis = normal.cross(xyDir) - else: - axis = edge.Placement.Rotation.Axis - angle = -1*edge.Placement.Rotation.Angle*FreeCAD.Units.Radian - if axis == Vector (0.0, 0.0, 0.0): - axis = Vector (0.0, 0.0, 1.0) - if angle: - edge.rotate(base, axis, angle) - if isinstance(edge.Curve,Part.Line): - return Part.LineSegment(edge.Curve,edge.FirstParameter,edge.LastParameter) - elif make_arc and isinstance(edge.Curve,Part.Circle) and not edge.Closed: - return Part.ArcOfCircle(edge.Curve, edge.FirstParameter, - edge.LastParameter,edge.Curve.Axis.z>0) - elif make_arc and isinstance(edge.Curve,Part.Ellipse) and not edge.Closed: - return Part.ArcOfEllipse(edge.Curve, edge.FirstParameter, - edge.LastParameter,edge.Curve.Axis.z>0) - return edge.Curve +from draftgeoutils.edges import orientEdge def mirror(point, edge): @@ -489,20 +454,7 @@ def isClockwise(edge, ref=None): return True -def isSameLine(e1, e2): - """isSameLine(e1,e2): return True if the 2 edges are lines and have the same - points""" - if not isinstance(e1.Curve,Part.LineSegment): - return False - if not isinstance(e2.Curve,Part.LineSegment): - return False - if (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[-1].Point)): - return True - elif (DraftVecUtils.equals(e1.Vertexes[-1].Point,e2.Vertexes[0].Point)) and \ - (DraftVecUtils.equals(e1.Vertexes[0].Point,e2.Vertexes[-1].Point)): - return True - return False +from draftgeoutils.edges import isSameLine def isWideAngle(edge): @@ -568,14 +520,7 @@ def getBoundary(shape): return bound -def isLine(bsp): - """Return True if the given BSpline curve is a straight line.""" - step = bsp.LastParameter/10 - b = bsp.tangent(0) - for i in range(10): - if bsp.tangent(i*step) != b: - return False - return True +from draftgeoutils.edges import isLine def sortEdges(edges): @@ -746,28 +691,7 @@ def sortEdgesOld(lEdges, aVertex=None): return [] -def invert(shape): - """invert(edge): returns an inverted copy of this edge or wire""" - if shape.ShapeType == "Wire": - edges = [invert(edge) for edge in shape.OrderedEdges] - edges.reverse() - return Part.Wire(edges) - elif shape.ShapeType == "Edge": - if len(shape.Vertexes) == 1: - return shape - if geomType(shape) == "Line": - return Part.LineSegment(shape.Vertexes[-1].Point,shape.Vertexes[0].Point).toShape() - elif geomType(shape) == "Circle": - mp = findMidpoint(shape) - return Part.Arc(shape.Vertexes[-1].Point,mp,shape.Vertexes[0].Point).toShape() - elif geomType(shape) in ["BSplineCurve","BezierCurve"]: - if isLine(shape.Curve): - return Part.LineSegment(shape.Vertexes[-1].Point,shape.Vertexes[0].Point).toShape() - print("DraftGeomUtils.invert: unable to invert",shape.Curve) - return shape - else: - print("DraftGeomUtils.invert: unable to handle",shape.ShapeType) - return shape +from draftgeoutils.edges import invert def flattenWire(wire): @@ -907,35 +831,7 @@ def superWire(edgeslist, closed=False): return Part.Wire(newedges) -def findMidpoint(edge): - """Calculate the midpoint of an edge.""" - first = edge.Vertexes[0].Point - last = edge.Vertexes[-1].Point - if geomType(edge) == "Circle": - center = edge.Curve.Center - radius = edge.Curve.Radius - if len(edge.Vertexes) == 1: - # Circle - dv = first.sub(center) - dv = dv.negative() - return center.add(dv) - axis = edge.Curve.Axis - chord = last.sub(first) - perp = chord.cross(axis) - perp.normalize() - ray = first.sub(center) - apothem = ray.dot(perp) - sagitta = radius - apothem - startpoint = Vector.add(first, chord.multiply(0.5)) - endpoint = DraftVecUtils.scaleTo(perp,sagitta) - return Vector.add(startpoint,endpoint) - - elif geomType(edge) == "Line": - halfedge = (last.sub(first)).multiply(.5) - return Vector.add(first,halfedge) - - else: - return None +from draftgeoutils.edges import findMidpoint def findPerpendicular(point, edgeslist, force=None): diff --git a/src/Mod/Draft/draftgeoutils/edges.py b/src/Mod/Draft/draftgeoutils/edges.py new file mode 100644 index 0000000000..3ddaf64fd0 --- /dev/null +++ b/src/Mod/Draft/draftgeoutils/edges.py @@ -0,0 +1,181 @@ +# *************************************************************************** +# * Copyright (c) 2009, 2010 Yorik van Havre * +# * Copyright (c) 2009, 2010 Ken Cline * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** +"""Provides various functions for using edges.""" +## @package edges +# \ingroup DRAFTGEOUTILS +# \brief Provides various functions for using edges. + +import lazy_loader.lazy_loader as lz + +import FreeCAD +import DraftVecUtils + +from draftgeoutils.general import geomType + +# Delay import of module until first use because it is heavy +Part = lz.LazyLoader("Part", globals(), "Part") + + +def findEdge(anEdge, aList): + """Return True if edge is found in list of edges.""" + for e in range(len(aList)): + if str(anEdge.Curve) == str(aList[e].Curve): + if DraftVecUtils.equals(anEdge.Vertexes[0].Point, + aList[e].Vertexes[0].Point): + if DraftVecUtils.equals(anEdge.Vertexes[-1].Point, + aList[e].Vertexes[-1].Point): + return e + return None + + +def orientEdge(edge, normal=None, make_arc=False): + """Re-orient the edge such that it is in the XY plane. + + Re-orients `edge` such that it is in the XY plane. + If `normal` is passed, this is used as the basis for the rotation, + otherwise the placement of `edge` is used. + """ + # This 'normalizes' the placement to the xy plane + edge = edge.copy() + xyDir = FreeCAD.Vector(0, 0, 1) + base = FreeCAD.Vector(0, 0, 0) + + if normal: + angle = DraftVecUtils.angle(normal, xyDir) * FreeCAD.Units.Radian + axis = normal.cross(xyDir) + else: + axis = edge.Placement.Rotation.Axis + angle = -1*edge.Placement.Rotation.Angle*FreeCAD.Units.Radian + if axis == FreeCAD.Vector(0.0, 0.0, 0.0): + axis = FreeCAD.Vector(0.0, 0.0, 1.0) + if angle: + edge.rotate(base, axis, angle) + if isinstance(edge.Curve, Part.Line): + return Part.LineSegment(edge.Curve, + edge.FirstParameter, + edge.LastParameter) + elif make_arc and isinstance(edge.Curve, Part.Circle) and not edge.Closed: + return Part.ArcOfCircle(edge.Curve, + edge.FirstParameter, + edge.LastParameter, + edge.Curve.Axis.z > 0) + elif make_arc and isinstance(edge.Curve, Part.Ellipse) and not edge.Closed: + return Part.ArcOfEllipse(edge.Curve, + edge.FirstParameter, + edge.LastParameter, + edge.Curve.Axis.z > 0) + return edge.Curve + + +def isSameLine(e1, e2): + """Return True if the 2 edges are lines and have the same points.""" + if not isinstance(e1.Curve, Part.LineSegment): + return False + if not isinstance(e2.Curve, Part.LineSegment): + return False + + if (DraftVecUtils.equals(e1.Vertexes[0].Point, + e2.Vertexes[0].Point) + and DraftVecUtils.equals(e1.Vertexes[-1].Point, + e2.Vertexes[-1].Point)): + return True + elif (DraftVecUtils.equals(e1.Vertexes[-1].Point, + e2.Vertexes[0].Point) + and DraftVecUtils.equals(e1.Vertexes[0].Point, + e2.Vertexes[-1].Point)): + return True + return False + + +def isLine(bspline): + """Return True if the given BSpline curve is a straight line.""" + step = bspline.LastParameter/10 + b = bspline.tangent(0) + + for i in range(10): + if bspline.tangent(i * step) != b: + return False + return True + + +def invert(shape): + """Return an inverted copy of the edge or wire contained in the shape.""" + if shape.ShapeType == "Wire": + edges = [invert(edge) for edge in shape.OrderedEdges] + edges.reverse() + return Part.Wire(edges) + elif shape.ShapeType == "Edge": + if len(shape.Vertexes) == 1: + return shape + if geomType(shape) == "Line": + return Part.LineSegment(shape.Vertexes[-1].Point, + shape.Vertexes[0].Point).toShape() + elif geomType(shape) == "Circle": + mp = findMidpoint(shape) + return Part.Arc(shape.Vertexes[-1].Point, + mp, + shape.Vertexes[0].Point).toShape() + elif geomType(shape) in ["BSplineCurve", "BezierCurve"]: + if isLine(shape.Curve): + return Part.LineSegment(shape.Vertexes[-1].Point, + shape.Vertexes[0].Point).toShape() + + print("DraftGeomUtils.invert: unable to invert", shape.Curve) + return shape + else: + print("DraftGeomUtils.invert: unable to handle", shape.ShapeType) + return shape + + +def findMidpoint(edge): + """Return the midpoint of a straight line or circular edge.""" + first = edge.Vertexes[0].Point + last = edge.Vertexes[-1].Point + + if geomType(edge) == "Circle": + center = edge.Curve.Center + radius = edge.Curve.Radius + if len(edge.Vertexes) == 1: + # Circle + dv = first.sub(center) + dv = dv.negative() + return center.add(dv) + + axis = edge.Curve.Axis + chord = last.sub(first) + perp = chord.cross(axis) + perp.normalize() + ray = first.sub(center) + apothem = ray.dot(perp) + sagitta = radius - apothem + startpoint = FreeCAD.Vector.add(first, chord.multiply(0.5)) + endpoint = DraftVecUtils.scaleTo(perp, sagitta) + return FreeCAD.Vector.add(startpoint, endpoint) + + elif geomType(edge) == "Line": + halfedge = (last.sub(first)).multiply(0.5) + return FreeCAD.Vector.add(first, halfedge) + + else: + return None