diff --git a/src/Mod/Draft/CMakeLists.txt b/src/Mod/Draft/CMakeLists.txt index 194e390b46..f2924a218f 100644 --- a/src/Mod/Draft/CMakeLists.txt +++ b/src/Mod/Draft/CMakeLists.txt @@ -28,6 +28,11 @@ SET(Draft_import importSVG.py ) +SET (Draft_geoutils + draftgeoutils/__init__.py + draftgeoutils/general.py +) + SET(Draft_tests drafttests/__init__.py drafttests/auxiliary.py @@ -247,6 +252,7 @@ SET(Draft_task_panels SET(Draft_SRCS_all ${Draft_SRCS_base} ${Draft_import} + ${Draft_geoutils} ${Draft_tests} ${Draft_utilities} ${Draft_functions} @@ -293,6 +299,7 @@ INSTALL( ) INSTALL(FILES ${Draft_tests} DESTINATION Mod/Draft/drafttests) +INSTALL(FILES ${Draft_geoutils} DESTINATION Mod/Draft/draftgeoutils) INSTALL(FILES ${Draft_utilities} DESTINATION Mod/Draft/draftutils) INSTALL(FILES ${Draft_functions} DESTINATION Mod/Draft/draftfunctions) INSTALL(FILES ${Draft_make_functions} DESTINATION Mod/Draft/draftmake) diff --git a/src/Mod/Draft/DraftGeomUtils.py b/src/Mod/Draft/DraftGeomUtils.py index 12fd58ff81..6a1d02e9a4 100644 --- a/src/Mod/Draft/DraftGeomUtils.py +++ b/src/Mod/Draft/DraftGeomUtils.py @@ -53,205 +53,47 @@ params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") # Generic functions ********************************************************* -def precision(): - """precision(): returns the Draft precision setting""" - # Set precision level with a cap to avoid overspecification that: - # 1 - whilst it is precise enough (e.g. that OCC would consider 2 points are coincident) - # (not sure what it should be 10 or otherwise); - # 2 - but FreeCAD/OCC can handle 'internally' (e.g. otherwise user may set something like - # 15 that the code would never consider 2 points are coincident as internal float is not that precise); - - precisionMax = 10 - precisionInt = params.GetInt("precision",6) - precisionInt = (precisionInt if precisionInt <=10 else precisionMax) - return precisionInt # return params.GetInt("precision",6) +from draftgeoutils.general import precision -def vec(edge): - """vec(edge) or vec(line): returns a vector from an edge or a Part.LineSegment""" - # if edge is not straight, you'll get strange results! - if isinstance(edge,Part.Shape): - return edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point) - elif isinstance(edge,Part.LineSegment): - return edge.EndPoint.sub(edge.StartPoint) - else: - return None +from draftgeoutils.general import vec -def edg(p1, p2): - """edg(Vector,Vector): returns an edge from 2 vectors""" - if isinstance(p1,FreeCAD.Vector) and isinstance(p2,FreeCAD.Vector): - if DraftVecUtils.equals(p1,p2): return None - else: return Part.LineSegment(p1,p2).toShape() +from draftgeoutils.general import edg -def getVerts(shape): - """getVerts(shape): returns a list containing vectors of each vertex of the shape""" - if not hasattr(shape,"Vertexes"): - return [] - p = [] - for v in shape.Vertexes: - p.append(v.Point) - return p +from draftgeoutils.general import getVerts -def v1(edge): - """v1(edge): returns the first point of an edge""" - return edge.Vertexes[0].Point +from draftgeoutils.general import v1 -def isNull(something): - """isNull(object): returns true if the given shape is null or the given placement is null or - if the given vector is (0,0,0)""" - if isinstance(something,Part.Shape): - return something.isNull() - elif isinstance(something,FreeCAD.Vector): - if something == Vector(0,0,0): - return True - else: - return False - elif isinstance(something,FreeCAD.Placement): - if (something.Base == Vector(0,0,0)) and (something.Rotation.Q == (0,0,0,1)): - return True - else: - return False +from draftgeoutils.general import isNull -def isPtOnEdge(pt, edge): - """isPtOnEdge(Vector,edge): Tests if a point is on an edge""" - v = Part.Vertex(pt) - try: - d = v.distToShape(edge) - except: - return False - else: - if d: - if round(d[0],precision()) == 0: - return True - return False +from draftgeoutils.general import isPtOnEdge -def hasCurves(shape): - """hasCurve(shape): checks if the given shape has curves""" - for e in shape.Edges: - if not isinstance(e.Curve,(Part.LineSegment,Part.Line)): - return True - return False +from draftgeoutils.general import hasCurves -def isAligned(edge, axis="x"): - """isAligned(edge,axis): checks if the given edge or line is aligned to the given axis (x, y or z)""" - if axis == "x": - if isinstance(edge,Part.Edge): - if len(edge.Vertexes) == 2: - if edge.Vertexes[0].X == edge.Vertexes[-1].X: - return True - elif isinstance(edge,Part.LineSegment): - if edge.StartPoint.x == edge.EndPoint.x: - return True - elif axis == "y": - if isinstance(edge,Part.Edge): - if len(edge.Vertexes) == 2: - if edge.Vertexes[0].Y == edge.Vertexes[-1].Y: - return True - elif isinstance(edge,Part.LineSegment): - if edge.StartPoint.y == edge.EndPoint.y: - return True - elif axis == "z": - if isinstance(edge,Part.Edge): - if len(edge.Vertexes) == 2: - if edge.Vertexes[0].Z == edge.Vertexes[-1].Z: - return True - elif isinstance(edge,Part.LineSegment): - if edge.StartPoint.z == edge.EndPoint.z: - return True - return False +from draftgeoutils.general import isAligned -def getQuad(face): - """getQuad(face): returns a list of 3 vectors (basepoint, Xdir, Ydir) if the face - is a quad, or None if not.""" - if len(face.Edges) != 4: - return None - v1 = vec(face.Edges[0]) - v2 = vec(face.Edges[1]) - v3 = vec(face.Edges[2]) - v4 = vec(face.Edges[3]) - angles90 = [round(math.pi*0.5,precision()),round(math.pi*1.5,precision())] - angles180 = [0,round(math.pi,precision()),round(math.pi*2,precision())] - for ov in [v2,v3,v4]: - if not (round(v1.getAngle(ov),precision()) in angles90+angles180): - return None - for ov in [v2,v3,v4]: - if round(v1.getAngle(ov),precision()) in angles90: - v1.normalize() - ov.normalize() - return [face.Edges[0].Vertexes[0].Point,v1,ov] +from draftgeoutils.general import getQuad -def areColinear(e1, e2): - """areColinear(e1,e2): returns True if both edges are colinear""" - if not isinstance(e1.Curve,(Part.LineSegment,Part.Line)): - return False - if not isinstance(e2.Curve,(Part.LineSegment,Part.Line)): - return False - v1 = vec(e1) - v2 = vec(e2) - a = round(v1.getAngle(v2),precision()) - if (a == 0) or (a == round(math.pi,precision())): - v3 = e2.Vertexes[0].Point.sub(e1.Vertexes[0].Point) - if DraftVecUtils.isNull(v3): - return True - else: - a2 = round(v1.getAngle(v3),precision()) - if (a2 == 0) or (a2 == round(math.pi,precision())): - return True - return False +from draftgeoutils.general import areColinear -def hasOnlyWires(shape): - """hasOnlyWires(shape): returns True if all the edges are inside a wire""" - ne = 0 - for w in shape.Wires: - ne += len(w.Edges) - if ne == len(shape.Edges): - return True - return False +from draftgeoutils.general import hasOnlyWires -def geomType(edge): - """returns the type of geom this edge is based on""" - try: - if isinstance(edge.Curve,(Part.LineSegment,Part.Line)): - return "Line" - elif isinstance(edge.Curve,Part.Circle): - return "Circle" - elif isinstance(edge.Curve,Part.BSplineCurve): - return "BSplineCurve" - elif isinstance(edge.Curve,Part.BezierCurve): - return "BezierCurve" - elif isinstance(edge.Curve,Part.Ellipse): - return "Ellipse" - else: - return "Unknown" - except: - return "Unknown" +from draftgeoutils.general import geomType -def isValidPath(shape): - """isValidPath(shape): returns True if the shape can be used as an extrusion path""" - if shape.isNull(): - return False - if shape.Faces: - return False - if len(shape.Wires) > 1: - return False - if shape.Wires: - if shape.Wires[0].isClosed(): - return False - if shape.isClosed(): - return False - return True +from draftgeoutils.general import isValidPath + # edge functions ************************************************************* diff --git a/src/Mod/Draft/draftgeoutils/__init__.py b/src/Mod/Draft/draftgeoutils/__init__.py new file mode 100644 index 0000000000..3dbd9f9f52 --- /dev/null +++ b/src/Mod/Draft/draftgeoutils/__init__.py @@ -0,0 +1,40 @@ +# *************************************************************************** +# * 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 * +# * * +# *************************************************************************** +"""Modules that contain functions that use and manipulate shapes. + +These functions provide support for dealing with the custom objects +defined within the workbench. +The functions are meant to be used in the creation step of the objects, +or to manipulate the created shapes, principally by the functions +in the `draftmake` package. + +These functions should deal with the internal shapes of the objects, +and their special properties; they shouldn't be very generic. + +These functions may be useful for other programmers in their own macros +or workbenches. These functions may not necessarily be exposed as +part of the Draft workbench programming interface yet. + +These functions were previously defined in the big `DraftGeomUtils` module. +""" diff --git a/src/Mod/Draft/draftgeoutils/general.py b/src/Mod/Draft/draftgeoutils/general.py new file mode 100644 index 0000000000..1043b5f38d --- /dev/null +++ b/src/Mod/Draft/draftgeoutils/general.py @@ -0,0 +1,267 @@ +# *************************************************************************** +# * 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 general functions for shape operations.""" +## @package general +# \ingroup DRAFTGEOUTILS +# \brief Provides basic functions for shape operations. + +import math +import lazy_loader.lazy_loader as lz + +import FreeCAD +import DraftVecUtils + +# Delay import of module until first use because it is heavy +Part = lz.LazyLoader("Part", globals(), "Part") + +params = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft") + + +def precision(): + """Return the Draft precision setting.""" + # Set precision level with a cap to avoid overspecification that: + # 1 - whilst it is precise enough (e.g. that OCC would consider + # 2 points are coincident) + # (not sure what it should be 10 or otherwise); + # 2 - but FreeCAD/OCC can handle 'internally' + # (e.g. otherwise user may set something like + # 15 that the code would never consider 2 points are coincident + # as internal float is not that precise) + precisionMax = 10 + precisionInt = params.GetInt("precision", 6) + precisionInt = (precisionInt if precisionInt <= 10 else precisionMax) + return precisionInt # return params.GetInt("precision",6) + + +def vec(edge): + """Return a vector from an edge or a Part.LineSegment.""" + # if edge is not straight, you'll get strange results! + if isinstance(edge, Part.Shape): + return edge.Vertexes[-1].Point.sub(edge.Vertexes[0].Point) + elif isinstance(edge, Part.LineSegment): + return edge.EndPoint.sub(edge.StartPoint) + else: + return None + + +def edg(p1, p2): + """Return an edge from 2 vectors.""" + if isinstance(p1, FreeCAD.Vector) and isinstance(p2, FreeCAD.Vector): + if DraftVecUtils.equals(p1, p2): + return None + + return Part.LineSegment(p1, p2).toShape() + + +def getVerts(shape): + """Return a list containing vectors of each vertex of the shape.""" + if not hasattr(shape, "Vertexes"): + return [] + + p = [] + for v in shape.Vertexes: + p.append(v.Point) + return p + + +def v1(edge): + """Return the first point of an edge.""" + return edge.Vertexes[0].Point + + +def isNull(something): + """Return True if the given shape, vector, or placement is Null. + + If the vector is (0, 0, 0), it will return True. + """ + if isinstance(something, Part.Shape): + return something.isNull() + elif isinstance(something, FreeCAD.Vector): + if something == FreeCAD.Vector(0, 0, 0): + return True + else: + return False + elif isinstance(something, FreeCAD.Placement): + if (something.Base == FreeCAD.Vector(0, 0, 0) + and something.Rotation.Q == (0, 0, 0, 1)): + return True + else: + return False + + +def isPtOnEdge(pt, edge): + """Test if a point lies on an edge.""" + v = Part.Vertex(pt) + try: + d = v.distToShape(edge) + except Part.OCCError: + return False + else: + if d: + if round(d[0], precision()) == 0: + return True + return False + + +def hasCurves(shape): + """Check if the given shape has curves.""" + for e in shape.Edges: + if not isinstance(e.Curve, (Part.LineSegment, Part.Line)): + return True + return False + + +def isAligned(edge, axis="x"): + """Check if the given edge or line is aligned to the given axis. + + The axis can be 'x', 'y' or 'z'. + """ + if axis == "x": + if isinstance(edge, Part.Edge): + if len(edge.Vertexes) == 2: + if edge.Vertexes[0].X == edge.Vertexes[-1].X: + return True + elif isinstance(edge, Part.LineSegment): + if edge.StartPoint.x == edge.EndPoint.x: + return True + + elif axis == "y": + if isinstance(edge, Part.Edge): + if len(edge.Vertexes) == 2: + if edge.Vertexes[0].Y == edge.Vertexes[-1].Y: + return True + elif isinstance(edge, Part.LineSegment): + if edge.StartPoint.y == edge.EndPoint.y: + return True + + elif axis == "z": + if isinstance(edge, Part.Edge): + if len(edge.Vertexes) == 2: + if edge.Vertexes[0].Z == edge.Vertexes[-1].Z: + return True + elif isinstance(edge, Part.LineSegment): + if edge.StartPoint.z == edge.EndPoint.z: + return True + return False + + +def getQuad(face): + """Return a list of 3 vectors if the face is a quad, ortherwise None. + + Returns + ------- + basepoint, Xdir, Ydir + If the face is a quad. + + None + If the face is not a quad. + """ + if len(face.Edges) != 4: + return None + + v1 = vec(face.Edges[0]) + v2 = vec(face.Edges[1]) + v3 = vec(face.Edges[2]) + v4 = vec(face.Edges[3]) + angles90 = [round(math.pi*0.5, precision()), + round(math.pi*1.5, precision())] + + angles180 = [0, + round(math.pi, precision()), + round(math.pi*2, precision())] + for ov in [v2, v3, v4]: + if not (round(v1.getAngle(ov), precision()) in angles90 + angles180): + return None + + for ov in [v2, v3, v4]: + if round(v1.getAngle(ov), precision()) in angles90: + v1.normalize() + ov.normalize() + return [face.Edges[0].Vertexes[0].Point, v1, ov] + + +def areColinear(e1, e2): + """Return True if both edges are colinear.""" + if not isinstance(e1.Curve, (Part.LineSegment, Part.Line)): + return False + if not isinstance(e2.Curve, (Part.LineSegment, Part.Line)): + return False + + v1 = vec(e1) + v2 = vec(e2) + a = round(v1.getAngle(v2), precision()) + if (a == 0) or (a == round(math.pi, precision())): + v3 = e2.Vertexes[0].Point.sub(e1.Vertexes[0].Point) + if DraftVecUtils.isNull(v3): + return True + else: + a2 = round(v1.getAngle(v3), precision()) + if (a2 == 0) or (a2 == round(math.pi, precision())): + return True + return False + + +def hasOnlyWires(shape): + """Return True if all edges are inside a wire.""" + ne = 0 + for w in shape.Wires: + ne += len(w.Edges) + if ne == len(shape.Edges): + return True + return False + + +def geomType(edge): + """Return the type of geometry this edge is based on.""" + try: + if isinstance(edge.Curve, (Part.LineSegment, Part.Line)): + return "Line" + elif isinstance(edge.Curve, Part.Circle): + return "Circle" + elif isinstance(edge.Curve, Part.BSplineCurve): + return "BSplineCurve" + elif isinstance(edge.Curve, Part.BezierCurve): + return "BezierCurve" + elif isinstance(edge.Curve, Part.Ellipse): + return "Ellipse" + else: + return "Unknown" + except TypeError: + return "Unknown" + + +def isValidPath(shape): + """Return True if the shape can be used as an extrusion path.""" + if shape.isNull(): + return False + if shape.Faces: + return False + if len(shape.Wires) > 1: + return False + if shape.Wires: + if shape.Wires[0].isClosed(): + return False + if shape.isClosed(): + return False + return True