''' Collection of helper function to extract geometry properties from OCC elements Most of the functions are borrowed directly from assembly2lib.py or lib3D.py in assembly2 ''' import math from collections import namedtuple import FreeCAD, FreeCADGui, Part, Draft import numpy as np from .FCADLogger import FCADLogger rootlogger = FCADLogger('asm3') logger = FCADLogger('asm3.main',parent=rootlogger) guilogger = FCADLogger('asm3.gui',parent=rootlogger) cstrlogger = FCADLogger('asm3.cstr',parent=rootlogger) syslogger = FCADLogger('asm3.sys',parent=rootlogger) proxylogger = FCADLogger('asm3.proxy',parent=rootlogger) import sys, os modulePath = os.path.dirname(os.path.realpath(__file__)) from PySide.QtCore import Qt from PySide.QtGui import QIcon, QPainter, QPixmap iconPath = os.path.join(modulePath,'Gui','Resources','icons') pixmapDisabled = QPixmap(os.path.join(iconPath,'Assembly_Disabled.svg')) iconSize = (16,16) def getIcon(obj,disabled=False,path=None): if not path: path = iconPath if not getattr(obj,'_icon',None): obj._icon = QIcon(os.path.join(path,obj._iconName)) if not disabled: return obj._icon if not getattr(obj,'_iconDisabled',None): pixmap = obj._icon.pixmap(*iconSize,mode=QIcon.Disabled) icon = QIcon(pixmapDisabled) icon.paint(QPainter(pixmap), 0,0,iconSize[0],iconSize[1],Qt.AlignCenter) obj._iconDisabled = QIcon(pixmap) return obj._iconDisabled def addIconToFCAD(iconFile,path=None): if not path: path = iconPath try: path = os.path.join(path,iconFile) FreeCADGui.addIcon(path,path) except AssertionError: pass return path def objName(obj): try: return getattr(obj,'FullName',obj.Name) except Exception: return '?' def isLine(param): if hasattr(Part,"LineSegment"): return isinstance(param,(Part.Line,Part.LineSegment)) else: return isinstance(param,Part.Line) def deduceSelectedElement(obj,subname): shape = obj.getSubObject(subname) if not shape: return count = shape.countElement('Face') if count==1: return 'Face1' elif not count: count = shape.countElement('Edge') if count==1: return 'Edge1' elif not count: count = shape.countElement('Vertex') if count==1: return 'Vertex1' def getElementShape(obj,tp=None,transform=False,noElementMap=True): if not isinstance(obj,(tuple,list)): shape = obj else: shape,mat,sobj = Part.getShape(obj[0],subname=obj[1], needSubElement=True,retType=2, transform=transform,noElementMap=noElementMap) if not sobj: logger.trace('no sub object {}',obj) return if sobj.isDerivedFrom('App::Line'): if tp not in (None,Part.Shape,Part.Edge): logger.trace('wrong type of shape {}',obj) return size = sobj.ViewObject.Size shape = Part.makeLine(FreeCAD.Vector(-size,0,0), FreeCAD.Vector(size,0,0)) shape.transformShape(mat,False,True) return shape elif sobj.isDerivedFrom('App::Plane'): if tp not in (None, Part.Shape, Part.Face): logger.trace('wrong type of shape {}',obj) return size = sobj.ViewObject.Size shape = Part.makePlane(size*2,size*2, FreeCAD.Vector(-size,-size,0)) shape.transformShape(mat,False,True) return shape elif shape.isNull(): logger.trace('no shape {}',obj) return if not isinstance(shape,Part.Shape) or shape.isNull(): logger.trace('null shape {}',obj) return if not tp or isinstance(shape,tp): return shape elif isinstance(shape,(Part.Vertex,Part.Edge,Part.Face)): logger.trace('wrong shape type {}',obj) return elif tp is Part.Vertex: if shape.countElement('Edge'): return if shape.countElement('Vertex')==1: return shape.Vertex1 elif tp is Part.Edge: if shape.countElement('Face'): return if shape.countElement('Edge')==1: return shape.Edge1 elif tp is Part.Face: if shape.countElement('Face')==1: return shape.Face1 else: logger.trace('wrong shape type {}',obj) def isDraftWire(obj): proxy = getattr(obj,'Proxy',None) if isinstance(proxy,Draft._Wire) and \ not obj.Subdivisions and \ not obj.Base and \ not obj.Tool and \ obj.Points: return obj def isDraftCircle(obj): proxy = getattr(obj,'Proxy',None) if isinstance(proxy,Draft._Circle): return obj def isDraftObject(obj): o = isDraftWire(obj) if o: return o return isDraftCircle(obj) def isElement(obj): if not isinstance(obj,(tuple,list)): shape = obj else: sobj,_,shape = obj[0].getSubObject(obj[1],2) if not sobj: return if not shape: return sobj.TypeId in ('App::Line','App::Plane') if isinstance(obj,(Part.Vertex,Part.Face,Part.Edge)): return True if isinstance(shape,Part.Shape) and not shape.isNull(): return shape.countElement('Vertex')==1 or \ shape.countElement('Edge')==1 or \ shape.countElement('Face')==1 def isPlanar(obj): if isCircularEdge(obj): return True shape = getElementShape(obj,Part.Face) if not shape: return False elif str(shape.Surface) == '': return True elif hasattr(shape.Surface,'Radius'): return False elif str(shape.Surface).startswith('': return False else: _axis,_center,error=fit_rotation_axis_to_surface1(face.Surface) error_normalized = error / face.BoundBox.DiagonalLength return error_normalized < 10**-6 def isAxisOfPlane(obj): face = getElementShape(obj,Part.Face) if not face: return False if str(face.Surface) == '': return True else: _axis,_center,error=fit_rotation_axis_to_surface1(face.Surface) error_normalized = error / face.BoundBox.DiagonalLength return error_normalized < 10**-6 def isCircularEdge(obj): edge = getElementShape(obj,Part.Edge) if not edge: return False elif not hasattr(edge, 'Curve'): #issue 39 return False if hasattr( edge.Curve, 'Radius' ): return True elif isLine(edge.Curve): return False else: BSpline = edge.Curve.toBSpline() try: arcs = BSpline.toBiArcs(10**-6) except Exception: #FreeCAD exception thrown () return False if all( hasattr(a,'Center') for a in arcs ): centers = np.array([a.Center for a in arcs]) sigma = np.std( centers, axis=0 ) return max(sigma) < 10**-6 return False def isLinearEdge(obj): edge = getElementShape(obj,Part.Edge) if not edge: return False elif not hasattr(edge, 'Curve'): #issue 39 return False if isLine(edge.Curve): return True elif hasattr( edge.Curve, 'Radius' ): return False else: BSpline = edge.Curve.toBSpline() try: arcs = BSpline.toBiArcs(10**-6) except Exception: #FreeCAD exception thrown () return False if all(isLine(a) for a in arcs): lines = arcs D = np.array([L.tangent(0)[0] for L in lines]) #D(irections) return np.std( D, axis=0 ).max() < 10**-9 return False def isVertex(obj): return getElementShape(obj,Part.Vertex) is not None def hasCenter(_obj): # Any shape has no center? # return isVertex(obj) or isCircularEdge(obj) or \ # isAxisOfPlane(obj) or isSphericalSurface(obj) return True def isSphericalSurface(obj): face = getElementShape(obj,Part.Face) if not face: return False return str( face.Surface ).startswith('Sphere ') def getElementPos(obj): vertex = getElementShape(obj,Part.Vertex) if vertex: return vertex.Point face = getElementShape(obj,Part.Face) if face: surface = face.Surface if str(surface) == '': return face.BoundBox.Center # pos = surface.Position elif all( hasattr(surface,a) for a in ['Axis','Center','Radius'] ): return surface.Center elif str(surface).startswith('