def __init__(self, *args): T = gp_Trsf() if len(args) == 0: pass elif len(args) == 1: t = args[0] if isinstance(t, Vector): T.SetTranslationPart(t.wrapped) elif isinstance(t, Plane): cs = gp_Ax3(t.origin.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() elif isinstance(t, TopLoc_Location): self.wrapped = t return elif isinstance(t, gp_Trsf): T = t elif len(args) == 2: t, v = args cs = gp_Ax3(v.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() else: t, ax, angle = args T.SetRotation(gp_Ax1(Vector().toPnt(), ax.toDir()), angle * math.pi / 180.0) T.SetTranslationPart(t.wrapped) self.wrapped = TopLoc_Location(T)
def tessellate(self): self.vertices = [] self.triangles = [] self.normals = [] # global buffers p_buf = gp_Pnt() n_buf = gp_Vec() loc_buf = TopLoc_Location() offset = -1 # every line below is selected for performance. Do not introduce functions to "beautify" the code for face in get_faces(self.shape): if face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED: i1, i2 = 2, 1 else: i1, i2 = 1, 2 internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL poly = BRep_Tool.Triangulation_s(face, loc_buf) if poly is not None: Trsf = loc_buf.Transformation() # add vertices flat = [] for i in range(1, poly.NbNodes() + 1): flat.extend(poly.Node(i).Transformed(Trsf).Coord()) self.vertices.extend(flat) # add triangles flat = [] for i in range(1, poly.NbTriangles() + 1): coord = poly.Triangle(i).Get() flat.extend((coord[0] + offset, coord[i1] + offset, coord[i2] + offset)) self.triangles.extend(flat) # add normals if poly.HasUVNodes(): prop = BRepGProp_Face(face) flat = [] for i in range(1, poly.NbNodes() + 1): u, v = poly.UVNode(i).Coord() prop.Normal(u, v, p_buf, n_buf) if n_buf.SquareMagnitude() > 0: n_buf.Normalize() flat.extend(n_buf.Reverse().Coord( ) if internal else n_buf.Coord()) self.normals.extend(flat) offset += poly.NbNodes()
def compute_edges(self): edge_map = TopTools_IndexedMapOfShape() face_map = TopTools_IndexedDataMapOfShapeListOfShape() TopExp.MapShapes_s(self.shape, TopAbs_EDGE, edge_map) TopExp.MapShapesAndAncestors_s(self.shape, TopAbs_EDGE, TopAbs_FACE, face_map) for i in range(1, edge_map.Extent() + 1): edge = TopoDS.Edge_s(edge_map.FindKey(i)) face_list = face_map.FindFromKey(edge) if face_list.Extent() == 0: # print("no faces") continue loc = TopLoc_Location() poly = BRep_Tool.Polygon3D_s(edge, loc) if poly is not None: # print("Polygon3D successful") nodes = poly.Nodes() transf = loc.Transformation() v1 = None for j in range(1, poly.NbNodes() + 1): v2 = nodes.Value(j) v2.Transform(transf) v2 = v2.Coord() if v1 is not None: self.edges.append((v1, v2)) v1 = v2 else: face = TopoDS.Face_s(face_list.First()) triang = BRep_Tool.Triangulation_s(face, loc) poly = BRep_Tool.PolygonOnTriangulation_s(edge, triang, loc) if poly is None: continue indices = poly.Nodes() nodes = triang.Nodes() transf = loc.Transformation() v1 = None for j in range(indices.Lower(), indices.Upper() + 1): v2 = nodes.Value(indices.Value(j)) v2.Transform(transf) v2 = v2.Coord() if v1 is not None: self.edges.append((v1, v2)) v1 = v2
def toCAF(assy: AssemblyProtocol, coloredSTEP: bool = False) -> Tuple[TDF_Label, TDocStd_Document]: # prepare a doc app = XCAFApp_Application.GetApplication_s() doc = TDocStd_Document(TCollection_ExtendedString("XmlOcaf")) app.InitDocument(doc) tool = XCAFDoc_DocumentTool.ShapeTool_s(doc.Main()) tool.SetAutoNaming_s(False) ctool = XCAFDoc_DocumentTool.ColorTool_s(doc.Main()) # add root top = tool.NewShape() TDataStd_Name.Set_s(top, TCollection_ExtendedString("CQ assembly")) # add leafs and subassemblies subassys: Dict[str, Tuple[TDF_Label, Location]] = {} for k, v in assy.traverse(): # leaf part lab = tool.NewShape() tool.SetShape(lab, Compound.makeCompound(v.shapes).wrapped) setName(lab, f"{k}_part", tool) # assy part subassy = tool.NewShape() tool.AddComponent(subassy, lab, TopLoc_Location()) setName(subassy, k, tool) # handle colors - this logic is needed for proper STEP export color = v.color tmp = v if coloredSTEP: while not color and tmp.parent: tmp = tmp.parent color = tmp.color if color: setColor(lab, color, ctool) else: if color: setColor(subassy, color, ctool) subassys[k] = (subassy, v.loc) for ch in v.children: tool.AddComponent(subassy, subassys[ch.name][0], subassys[ch.name][1].wrapped) tool.AddComponent(top, subassys[assy.name][0], assy.loc.wrapped) tool.UpdateAssemblies() return top, doc
def compute_edges(self): edge_map = TopTools_IndexedMapOfShape() face_map = TopTools_IndexedDataMapOfShapeListOfShape() TopExp.MapShapes_s(self.shape, TopAbs_EDGE, edge_map) TopExp.MapShapesAndAncestors_s(self.shape, TopAbs_EDGE, TopAbs_FACE, face_map) for i in range(1, edge_map.Extent() + 1): edge = TopoDS.Edge_s(edge_map.FindKey(i)) face_list = face_map.FindFromKey(edge) if face_list.Extent() == 0: # print("no faces") continue loc = TopLoc_Location() face = TopoDS.Face_s(face_list.First()) triangle = BRep_Tool.Triangulation_s(face, loc) poly = BRep_Tool.PolygonOnTriangulation_s(edge, triangle, loc) if poly is None: continue if hasattr(poly, "Node"): # OCCT > 7.5 nrange = range(1, poly.NbNodes() + 1) index = poly.Node else: # OCCT == 7.5 indices = poly.Nodes() nrange = range(indices.Lower(), indices.Upper() + 1) index = indices.Value transf = loc.Transformation() v1 = None for j in nrange: v2 = triangle.Node(index(j)).Transformed(transf).Coord() if v1 is not None: self.edges.append((v1, v2)) v1 = v2
def __init__(self, *args): T = gp_Trsf() if len(args) == 0: pass elif len(args) == 1: t = args[0] if isinstance(t, Vector): T.SetTranslationPart(t.wrapped) elif isinstance(t, Plane): cs = gp_Ax3(t.origin.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() elif isinstance(t, TopLoc_Location): self.wrapped = t return elif isinstance(t, gp_Trsf): T = t elif isinstance(t, (tuple, list)): raise TypeError( "A tuple or list is not a valid parameter, use a Vector instead." ) else: raise TypeError("Unexpected parameters") elif len(args) == 2: t, v = args cs = gp_Ax3(v.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() else: t, ax, angle = args T.SetRotation(gp_Ax1(Vector().toPnt(), ax.toDir()), angle * math.pi / 180.0) T.SetTranslationPart(t.wrapped) self.wrapped = TopLoc_Location(T)
class Location(object): """Location in 3D space. Depending on usage can be absolute or relative. This class wraps the TopLoc_Location class from OCCT. It can be used to move Shape objects in both relative and absolute manner. It is the preferred type to locate objects in CQ. """ wrapped: TopLoc_Location @overload def __init__(self) -> None: """Empty location with not rotation or translation with respect to the original location.""" ... @overload def __init__(self, t: Vector) -> None: """Location with translation t with respect to the orignal location.""" ... @overload def __init__(self, t: Plane) -> None: """Location corresponding to the location of the Plane t.""" ... @overload def __init__(self, t: Plane, v: Vector) -> None: """Location corresponding to the angular location of the Plane t with translation v.""" ... @overload def __init__(self, t: TopLoc_Location) -> None: """Location wrapping the low-level TopLoc_Location object t""" ... @overload def __init__(self, t: gp_Trsf) -> None: """Location wrapping the low-level gp_Trsf object t""" ... @overload def __init__(self, t: Vector, ax: Vector, angle: float) -> None: """Location with translation t and rotation around ax by angle with respect to the original location.""" ... def __init__(self, *args): T = gp_Trsf() if len(args) == 0: pass elif len(args) == 1: t = args[0] if isinstance(t, Vector): T.SetTranslationPart(t.wrapped) elif isinstance(t, Plane): cs = gp_Ax3(t.origin.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() elif isinstance(t, TopLoc_Location): self.wrapped = t return elif isinstance(t, gp_Trsf): T = t elif len(args) == 2: t, v = args cs = gp_Ax3(v.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() else: t, ax, angle = args T.SetRotation(gp_Ax1(Vector().toPnt(), ax.toDir()), angle * math.pi / 180.0) T.SetTranslationPart(t.wrapped) self.wrapped = TopLoc_Location(T) @property def inverse(self) -> "Location": return Location(self.wrapped.Inverted()) def __mul__(self, other: "Location") -> "Location": return Location(self.wrapped * other.wrapped)
def tessellate(self): self.vertices = array("f") self.triangles = array("f") self.normals = array("f") # global buffers p_buf = gp_Pnt() n_buf = gp_Vec() loc_buf = TopLoc_Location() offset = -1 # every line below is selected for performance. Do not introduce functions to "beautify" the code for face in get_faces(self.shape): if face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED: i1, i2 = 2, 1 else: i1, i2 = 1, 2 internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL poly = BRep_Tool.Triangulation_s(face, loc_buf) if poly is not None: Trsf = loc_buf.Transformation() # add vertices # [node.Transformed(Trsf).Coord() for node in poly.Nodes()] is 5-8 times slower! items = poly.Nodes() coords = [items.Value(i).Transformed(Trsf).Coord() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for coord in coords: flat += coord self.vertices.extend(flat) # add triangles items = poly.Triangles() coords = [items.Value(i).Get() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for coord in coords: flat += (coord[0] + offset, coord[i1] + offset, coord[i2] + offset) self.triangles.extend(flat) # add normals if poly.HasUVNodes(): def extract(uv0, uv1): prop.Normal(uv0, uv1, p_buf, n_buf) if n_buf.SquareMagnitude() > 0: n_buf.Normalize() return n_buf.Reverse().Coord() if internal else n_buf.Coord() prop = BRepGProp_Face(face) items = poly.UVNodes() uvs = [items.Value(i).Coord() for i in range(items.Lower(), items.Upper() + 1)] flat = [] for uv1, uv2 in uvs: flat += extract(uv1, uv2) self.normals.extend(flat) offset += poly.NbNodes()
def tq_to_loc(t, q): T = gp_Trsf() Q = gp_Quaternion(*q) V = gp_Vec(*t) T.SetTransformation(Q, V) return TopLoc_Location(T)
class Location(object): """Location in 3D space. Depending on usage can be absolute or relative. This class wraps the TopLoc_Location class from OCCT. It can be used to move Shape objects in both relative and absolute manner. It is the preferred type to locate objects in CQ. """ wrapped: TopLoc_Location @overload def __init__(self) -> None: """Empty location with not rotation or translation with respect to the original location.""" ... @overload def __init__(self, t: Vector) -> None: """Location with translation t with respect to the original location.""" ... @overload def __init__(self, t: Plane) -> None: """Location corresponding to the location of the Plane t.""" ... @overload def __init__(self, t: Plane, v: Vector) -> None: """Location corresponding to the angular location of the Plane t with translation v.""" ... @overload def __init__(self, t: TopLoc_Location) -> None: """Location wrapping the low-level TopLoc_Location object t""" ... @overload def __init__(self, t: gp_Trsf) -> None: """Location wrapping the low-level gp_Trsf object t""" ... @overload def __init__(self, t: Vector, ax: Vector, angle: float) -> None: """Location with translation t and rotation around ax by angle with respect to the original location.""" ... def __init__(self, *args): T = gp_Trsf() if len(args) == 0: pass elif len(args) == 1: t = args[0] if isinstance(t, Vector): T.SetTranslationPart(t.wrapped) elif isinstance(t, Plane): cs = gp_Ax3(t.origin.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() elif isinstance(t, TopLoc_Location): self.wrapped = t return elif isinstance(t, gp_Trsf): T = t elif isinstance(t, (tuple, list)): raise TypeError( "A tuple or list is not a valid parameter, use a Vector instead." ) else: raise TypeError("Unexpected parameters") elif len(args) == 2: t, v = args cs = gp_Ax3(v.toPnt(), t.zDir.toDir(), t.xDir.toDir()) T.SetTransformation(cs) T.Invert() else: t, ax, angle = args T.SetRotation(gp_Ax1(Vector().toPnt(), ax.toDir()), angle * math.pi / 180.0) T.SetTranslationPart(t.wrapped) self.wrapped = TopLoc_Location(T) @property def inverse(self) -> "Location": return Location(self.wrapped.Inverted()) def __mul__(self, other: "Location") -> "Location": return Location(self.wrapped * other.wrapped) def toTuple( self ) -> Tuple[Tuple[float, float, float], Tuple[float, float, float]]: """Convert the location to a translation, rotation tuple.""" T = self.wrapped.Transformation() trans = T.TranslationPart() rot = T.GetRotation() rv_trans = (trans.X(), trans.Y(), trans.Z()) rv_rot = rot.GetEulerAngles(gp_EulerSequence.gp_Extrinsic_XYZ) return rv_trans, rv_rot
def tessellate(shape, tolerance: float, angularTolerance: float = 0.1): # Remove previous mesh data BRepTools.Clean_s(shape) triangulated = BRepTools.Triangulation_s(shape, tolerance) if not triangulated: # this will add mesh data to the shape and prevent calculating an exact bounding box after this call BRepMesh_IncrementalMesh(shape, tolerance, True, angularTolerance) vertices = [] triangles = [] normals = [] offset = 0 for face in get_faces(shape): loc = TopLoc_Location() poly = BRep_Tool.Triangulation_s(face, loc) Trsf = loc.Transformation() reverse = face.Orientation() == TopAbs_Orientation.TopAbs_REVERSED internal = face.Orientation() == TopAbs_Orientation.TopAbs_INTERNAL # add vertices if poly is not None: vertices += [(v.X(), v.Y(), v.Z()) for v in (v.Transformed(Trsf) for v in poly.Nodes())] # add triangles triangles += [( t.Value(1) + offset - 1, t.Value(3 if reverse else 2) + offset - 1, t.Value(2 if reverse else 3) + offset - 1, ) for t in poly.Triangles()] # add normals if poly.HasUVNodes(): prop = BRepGProp_Face(face) uvnodes = poly.UVNodes() for uvnode in uvnodes: p = gp_Pnt() n = gp_Vec() prop.Normal(uvnode.X(), uvnode.Y(), p, n) if n.SquareMagnitude() > 0: n.Normalize() if internal: n.Reverse() normals.append((n.X(), n.Y(), n.Z())) offset += poly.NbNodes() if not triangulated: # Remove the mesh data again BRepTools.Clean_s(shape) return ( np.asarray(vertices, dtype=np.float32), np.asarray(triangles, dtype=np.uint32), np.asarray(normals, dtype=np.float32), )