def group_planes_by_axis(shape): """[summary] Extract all planes from shape, and group them according to their normal vector. Arguments: shape {TopoDS_Shape} -- [description] Returns: {dict} -- key: normal vector as string) value: list of TopoDS_Shape(Plane) """ if shape is None: logging.warn("Input shape is None") pln_dict = None else: planeList = RecognizeTopo(shape).planes() pln_dict = {} for pln in planeList: gp_pln = BRepAdaptor_Surface(pln).Plane() normal = gp_pln.Axis().Direction() key = '%.6f,%.6f,%.6f' % (round(normal.X(), 6), round( normal.Y(), 6), round(normal.Z(), 6)) key = tuple([float(i) for i in key.split(',')]) if key not in pln_dict.keys(): pln_dict[key] = [pln] else: pln_dict[key].append(pln) return pln_dict
def match_singleHole(solid_add, solid_base): cyls_sel_base, cyls_sel_add, projPln = selectCylinderFromClosestPln( solid_add, solid_base) pntList_base = [] for i in cyls_sel_base: loc = BRepAdaptor_Surface(i).Cylinder().Location() pnt = gp_Pnt(loc.X(), loc.Y(), loc.Z()) pntList_base.append(pnt) pntList_add = [] for i in cyls_sel_add: loc = BRepAdaptor_Surface(i).Cylinder().Location() pnt = gp_Pnt(loc.X(), loc.Y(), loc.Z()) pntList_add.append(pnt) pntList_base_proj = [ ais_ProjectPointOnPlane(pnt, projPln) for pnt in pntList_base ] pntList_add_proj = [ ais_ProjectPointOnPlane(pnt, projPln) for pnt in pntList_add ] mvVec = get_shortest_vec(pntList_add_proj, pntList_base_proj) trsf = gp_Trsf() trsf.SetTranslation(mvVec) solid_add.Move(TopLoc_Location(trsf))
def filter_cylinder_by_position(cylList_from_solidAdd, projPln, dist_tol=0.3): """Project all points of cylinders' axis into the projPln, and return the projected point on the projPln Arguments: cylList_from_solidAdd {[type]} -- [description] projPln {[type]} -- [description] Keyword Arguments: dist_tol {float} -- [description] (default: {0.3}) Returns: [type] -- [description] """ # project the first cylinder to get the point. Which is needed for the later distance calculation initPnt = ais_ProjectPointOnPlane( BRepAdaptor_Surface(cylList_from_solidAdd[0], True).Cylinder().Location(), projPln) projPntList = [initPnt] for cyl in cylList_from_solidAdd: newPnt = ais_ProjectPointOnPlane( BRepAdaptor_Surface(cyl, True).Cylinder().Location(), projPln) isNewPnt = True # check whehter the projected point overlaps other points for pnt in projPntList: if newPnt.Distance(pnt) < dist_tol: isNewPnt = False if isNewPnt: projPntList.append(newPnt) return projPntList
def recognize_face(a_face): """ Takes a TopoDS shape and tries to identify its nature whether it is a plane a cylinder a torus etc. if a plane, returns the normal if a cylinder, returns the radius """ surf = BRepAdaptor_Surface(a_face, True) surf_type = surf.GetType() if surf_type == GeomAbs_Plane: print("--> plane") # look for the properties of the plane # first get the related gp_Pln gp_pln = surf.Plane() location = gp_pln.Location() # a point of the plane normal = gp_pln.Axis().Direction() # the plane normal # then export location and normal to the console output print("--> Location (global coordinates)", location.X(), location.Y(), location.Z()) print("--> Normal (global coordinates)", normal.X(), normal.Y(), normal.Z()) elif surf_type == GeomAbs_Cylinder: print("--> cylinder") # look for the properties of the cylinder # first get the related gp_Cyl gp_cyl = surf.Cylinder() location = gp_cyl.Location() # a point of the axis axis = gp_cyl.Axis().Direction() # the cylinder axis # then export location and normal to the console output print("--> Location (global coordinates)", location.X(), location.Y(), location.Z()) print("--> Axis (global coordinates)", axis.X(), axis.Y(), axis.Z()) else: # TODO there are plenty other type that can be checked # see documentation for the BRepAdaptor class # https://www.opencascade.com/doc/occt-6.9.1/refman/html/class_b_rep_adaptor___surface.html print("not implemented")
def Shape(self): ball_vecs = [] for i in self.balls: ball_vecs.append(gp_Vec(i.getPnt().XYZ())) v_ball_to_ball = ball_vecs[1] - ball_vecs[0] ax = gp_Ax2(gp_Pnt(ball_vecs[0].XYZ()), gp_Dir(v_ball_to_ball.XYZ())) mcyl = BRepPrimAPI_MakeCylinder(ax, self.d_o / 2.0, v_ball_to_ball.Magnitude()) cyl = mcyl.Shape() mch = BRepFilletAPI_MakeChamfer(cyl) endFaces = [] for face in Topo(cyl).faces(): adaptor = BRepAdaptor_Surface(face) if adaptor.GetType() == GeomAbs_Plane: endFaces.append(face) for edge in Topo(face).edges(): mch.Add(self.chamfer_distance, edge, face) try: chamferedCyl = mch.Shape() except: chamferedCyl = cyl print("chamfer on ForceTransferCylinder failed!") mc = BRepAlgoAPI_Cut(chamferedCyl, self.balls[0].Shape()) mc = BRepAlgoAPI_Cut(mc.Shape(), self.balls[1].Shape()) return mc.Shape()
def pipe_segment_to_edge(compound, uid=None): """ if it is two circles with linear face segments, the end sections will have N - 2 edges""" the_topo = Topo(compound) faces = [Face(f) for f in the_topo.faces()] faces.sort(reverse=True, key=lambda x: len(x.edges())) areas = [] verts = [] for f1 in faces[:2]: surf = BRepAdaptor_Surface(f1, True) surf_type = surf.GetType() areas.append(f1.area) uv, pnt = f1.mid_point() if surf_type == GeomAbs_Plane: gp_pln = surf.Plane() normal = f1.normal gppln2 = gp_Pln(pnt, normal) v = MEPConnector(gppln2) verts.append(v) radius = math.sqrt(sum(areas)) / (2 * math.pi) edge = MEPCurve(*verts) return MEPSegment(edge, radius, uid=uid)
def get_neighbor_vector(hole_cyl, cylList, projPln): vecList = [] pnt_hole = BRepAdaptor_Surface(hole_cyl, True).Cylinder().Location() pnt_start = ais_ProjectPointOnPlane(pnt_hole, projPln) for cyl in cylList: pnt_cyl = BRepAdaptor_Surface(cyl, True).Cylinder().Location() pnt_end = ais_ProjectPointOnPlane(pnt_cyl, projPln) vecList.append(gp_Vec(pnt_start, pnt_end)) return vecList
def __init__(self, topods_shp, proj_pln=None): self.shp = topods_shp adaptor = BRepAdaptor_Surface(self.shp) if adaptor.GetType() == GeomAbs_Cylinder: self.cyl = adaptor.Cylinder() self.axis = self.cyl.Axis() self.direction = self.axis.Direction() self.location = self.cyl.Location() self.neighborIsSet = False else: logging.warning('Wrong shape type, input should be cylinder') if proj_pln is not None: self.setProjPln(proj_pln)
def group_cylinders_byAxisDir(cylinders, anglTolDeg=5, roundDigit=6, groupParallelAx=True): logging.debug('Entering group_cylinders_byAxisDir') cyl_ax = {} for cylinder in cylinders: cyl = BRepAdaptor_Surface(cylinder, True).Cylinder() axis = cyl.Axis() key = (round(axis.Direction().X(), roundDigit), round(axis.Direction().Y(), roundDigit), round(axis.Direction().Z(), roundDigit)) if key in cyl_ax.keys(): cyl_ax[key].append(cylinder) else: cyl_ax[key] = [cylinder] for key in cyl_ax.keys(): listOflist = list(find_full_cylinder(cyl_ax[key]).values()) cyl_ax[key] = [k for i in listOflist for k in i] if groupParallelAx: axisKeyList = list(cyl_ax.keys()) combineList = [] j = 0 while len(axisKeyList) >= 1: ax1 = axisKeyList.pop(0) grp = [ax1] dir1 = gp_Dir(ax1[0], ax1[1], ax1[2]) while j < len(axisKeyList): ax2 = axisKeyList[j] dir2 = gp_Dir(ax2[0], ax2[1], ax2[2]) j += 1 if dir1.IsParallel(dir2, radians(anglTolDeg)): grp.append(ax2) j -= 1 axisKeyList.pop(j) combineList.append(grp) j = 0 cyl_grpByAx = {} for i in combineList: cyl_grpByAx[i[0]] = [] for j in i: cyl_grpByAx[i[0]] += cyl_ax[j] else: cyl_grpByAx = cyl_ax return cyl_grpByAx
def setPreselection(self, doc, obj, sub): sel = FreeCADGui.Selection.getSelectionEx() v = FreeCADGui.ActiveDocument.ActiveView d = FreeCADGui.ActiveDocument so = FreeCAD.ActiveDocument.getObject(obj) if not hasattr(so, "Shape"): return faces = so.Shape.Faces typ = sub[0:4] if not typ == "Face": return idx = int(sub[4:]) face = faces[idx - 1] txt = "" occface = Part.__toPythonOCC__(face) #surf = occface.Surface() ts = _TopoDS_face(occface) face_adaptor = BRepAdaptor_Surface(ts) face_type = face_adaptor.GetType() surf_txt = self.getSurfType(face_type) txt = self.getSurfEqua(surf_txt, face_adaptor) #t = GeomAdaptor_Surface(TopoDS_Shape(occface)).GetType() #FreeCAD.Console.PrintMessage("%s\n" % (str(t))) #if (str(face.Surface) == "<Plane object>"): #txt = "Plane" #elif (str(face.Surface) == "<Cylinder object>"): #txt = "Cylinder" #elif (str(face.Surface) == "<Toriod object>"): #txt = "Torus" pnt = v.getPoint(v.getCursorPos()) #FreeCAD.Console.PrintMessage("X %f Y %f Z %f\n" % (pnt.x, pnt.y, pnt.z)) adoc = FreeCAD.ActiveDocument objlist = adoc.findObjects("App::Annotation", "SurfInfo") if (objlist): objlist[0].LabelText = txt objlist[0].Position = (pnt.x, pnt.y, pnt.z)
def adaptor(self): if self._adaptor is not None and not self.is_dirty: pass else: self._adaptor = BRepAdaptor_Surface(self) self._adaptor_handle = BRepAdaptor_HSurface() self._adaptor_handle.Set(self._adaptor) return self._adaptor
def adaptor(self): if self._adaptor is not None and not self.is_dirty: pass else: from OCC.BRepAdaptor import BRepAdaptor_Surface, BRepAdaptor_HSurface self._adaptor = BRepAdaptor_Surface(self.topo) self._adaptor_handle = BRepAdaptor_HSurface() self._adaptor_handle.Set(self._adaptor) return self._adaptor
def identifySurf(self): occface = Part.__toPythonOCC__(self.__faceObj) ts = _TopoDS_face(occface) face_adaptor = BRepAdaptor_Surface(ts) face_type = face_adaptor.GetType() if (face_type == GeomAbs_Plane): self.__surfType = "Plane" elif (face_type == GeomAbs_Cylinder): self.__surfType = "Cylinder" elif (face_type == GeomAbs_Sphere): self.__surfType = "Sphere" elif (face_type == GeomAbs_Cone): self.__surfType = "Cone" elif (face_type == GeomAbs_Torus): self.__surfType = "Toroid" elif (face_type == GeomAbs_SurfaceOfRevolution): self.__surfType = "Revolution" else: self.__surfType = "Spline"
def __cylinder_group_radius(cyl_ax_grp, tol=0.1): logging.debug('Entering group_radius') keyList = list(cyl_ax_grp.keys()) cyl_rad_grp = {} for k in keyList: cylinders = cyl_ax_grp[k] skipList = [] for i in range(0, len(cylinders)): if i in skipList: continue cyl1 = BRepAdaptor_Surface(cylinders[i], True).Cylinder() radius1 = cyl1.Radius() key = '%s,r=%.3f' % (k, radius1) if key not in cyl_rad_grp: cyl_rad_grp[key] = [cylinders[i]] else: logging.warning('Error !!! Please check the logic again !') for j in range(i + 1, len(cylinders)): # logging.debug('i = %d, j = %d' % (i, j)) if j in skipList: continue cyl2 = BRepAdaptor_Surface(cylinders[j], True).Cylinder() radius2 = cyl2.Radius() if abs(radius1 - radius2) <= tol: # logging.debug('Same radius found') cyl_rad_grp[key].append(cylinders[j]) skipList.append(j) return cyl_rad_grp
def __group_coaxial_cylinders(cylinders, tol_ang=0.5, tol_lin=0.1, roundDigit=6): """According to cylinders' axis, categorize a list of cylinders into a dictionary by using its axis as a key. Arguments: cylinders {[TopoDS_Face,...]} -- list of TopoDS_Face tol_ang {float} -- [Unit - degree] the angle between these two axis below this value will be recognize as two parallel axis tol_lin Returns: {'string': [TopoDS_Face,...]} -- returns a dictionary, key is string of axis and location vector, value is list of TopoDS_Shape """ logging.debug('Entering group_coaxial') tol_rad = radians(tol_ang) skipList = [] cyl_ax_grp = {} for i in range(0, len(cylinders)): if i in skipList: continue cyl1 = BRepAdaptor_Surface(cylinders[i], True).Cylinder() axis1 = cyl1.Axis() location1 = cyl1.Location() axisDir1 = (round(axis1.Direction().X(), roundDigit), round(axis1.Direction().Y(), roundDigit), round(axis1.Direction().Z(), roundDigit)) cylLoc1 = (round(location1.X(), roundDigit), round(location1.Y(), roundDigit), round(location1.Z(), roundDigit)) key = (axisDir1, cylLoc1) if key not in cyl_ax_grp.keys(): cyl_ax_grp[key] = [cylinders[i]] else: logging.warning('Error !!! Please check the logic again !') for j in range(i + 1, len(cylinders)): # logging.debug('i = %d, j = %d' % (i, j)) if j in skipList: # logging.debug('skip !!') continue cyl2 = BRepAdaptor_Surface(cylinders[j]).Cylinder() axis2 = cyl2.Axis() if axis1.IsCoaxial(axis2, tol_rad, tol_lin) or axis1.IsCoaxial( axis2.Reversed(), tol_rad, tol_lin): # logging.debug('Coaxial !!') cyl_ax_grp[key].append(cylinders[j]) skipList.append(j) return cyl_ax_grp
def select_cyl_by_axisDir(full_cylinders, gpDir, ang_tol=0.5): """[summary] Arguments: full_cylinders {dictionary} -- {'string': List of TopoDS_Shape; GeomType in item of list:Cylinder} gpDir {gp_Dir} -- target axis direction ang_tol {float} -- angular tolerance expressed in Degrees. The angle between cylinder's axis and target axis dirction in this tol, will be consider parallel Returns: selected_cyl_List {list} -- List of TopoDS_Shape(cylinders) """ selected_cyl_List = [] for cylList in full_cylinders.values(): for cyl in cylList: axis = BRepAdaptor_Surface(cyl, True).Cylinder().Axis().Direction() if gpDir.IsParallel(axis, radians(0.5)): selected_cyl_List.append(cyl) return selected_cyl_List
def get_closest_parallel_planePair(solid_add, solid_base=None, init_min_dist=1000.0, xyplane_z_inMM=1215.0): """ Arguments: solid_add {TopoDS_Solid} -- The solid going to be added Keyword Arguments: solid_base {TopoDS_Solid} -- By default, solid_base will be xy-plane (default: {None}) init_min_dist {float} -- [description] (default: {10.0}) Returns: {dict} -- keyValues: miniDist, topoPair, geomPair, mvVec """ min_dist = init_min_dist ang_list = find_closest_normal_pair(solid_add, solid_base) if solid_base is None: solid_base = gen_boxSolidAsTable(height=xyplane_z_inMM) axisGrp1 = group_planes_by_axis(solid_base) axisGrp2 = group_planes_by_axis(solid_add) axisPair = ang_list['minAxisKeyPair'] for axKeyPair in axisPair: for topoPln1 in axisGrp1[axKeyPair[0]]: surf1 = BRepAdaptor_Surface(topoPln1, True) pln1 = surf1.Plane() # [ToDo] A better distance evaluation, how to select a correct point which is inside the wire or centerofmass of plane plnpnt1 = pln1.Location() for topoPln2 in axisGrp2[axKeyPair[1]]: surf2 = BRepAdaptor_Surface(topoPln2, True) pln2 = surf2.Plane() # rospy.logdebug('Distance:', pln2.Distance(plnpnt1)) dist = pln2.Distance(plnpnt1) if dist < min_dist: min_dist = dist minDistTopoPair = [topoPln1, topoPln2] minDistGeomPair = [pln1, pln2] p = gp_Pnt(0, 0, 0) p1 = ais_ProjectPointOnPlane(p, minDistGeomPair[0]) p2 = ais_ProjectPointOnPlane(p, minDistGeomPair[1]) # in order to remove small digits projPln = minDistGeomPair[0] mvVec = gp_Vec(p2, p1) projPlnNormal = gp_Vec(projPln.Axis().Direction()) mag = np.sign(mvVec.Dot(projPlnNormal)) * mvVec.Magnitude() mvVec = projPlnNormal.Normalized().Multiplied(mag) return { 'minDist': dist, 'topoPair': minDistTopoPair, 'geomPair': minDistGeomPair, 'mvVec': mvVec }
def group_cyl_byPln(solid, distTol=0.5, angTolDeg=5.0): cylinders = RecognizeTopo(solid).cylinders() cyl_dirGrp = group_cylinders_byAxisDir(cylinders, anglTolDeg=angTolDeg, groupParallelAx=True) cyl_dirGrpKeys = list(cyl_dirGrp.keys()) # group cylinders by plane CylinderGrpsFromDiffPln = {} j = 0 for dirKey in cyl_dirGrpKeys: CylinderGrpsFromDiffPln[dirKey] = [] parallel_cyl_grps = cyl_dirGrp[dirKey].copy() grp_cylinderOnSamePln = {} while len(parallel_cyl_grps) >= 1: # take out the first element for creating plane, and search the other cylinders on the same plane firstCylinder = parallel_cyl_grps.pop(0) # fitting a geomety surface to TopoDS_Surface brepCylinder = BRepAdaptor_Surface(firstCylinder).Cylinder() dir1 = brepCylinder.Axis().Direction() # location of cylinder is usually the location of local coordinate system, which is on the axis of cylinder loc1 = brepCylinder.Location() # create gp_Pln pln = gp_Pln(loc1, dir1) grp_cylinderOnSamePln[pln] = [firstCylinder] # Search the cylinders on the same pln, if yes, extract from parallel_cyl_grps # loop all elements in parallel_cyl_grps while j < len(parallel_cyl_grps): brepCylinder2 = BRepAdaptor_Surface( parallel_cyl_grps[j]).Cylinder() loc2 = brepCylinder2.Location() if pln.Distance(gp_Pnt(loc2.X(), loc2.Y(), loc2.Z())) < distTol: grp_cylinderOnSamePln[pln].append(parallel_cyl_grps[j]) parallel_cyl_grps.pop(j) else: j += 1 j = 0 CylinderGrpsFromDiffPln[dirKey] = grp_cylinderOnSamePln return CylinderGrpsFromDiffPln