From a8386d9fc1535867e9a4c839c6e5867bef702555 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Tue, 2 May 2017 16:49:22 +0200 Subject: [PATCH 1/9] Fix read iges shape --- pygem/igeshandler.py | 51 +++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/pygem/igeshandler.py b/pygem/igeshandler.py index b8a2d28..e1636de 100644 --- a/pygem/igeshandler.py +++ b/pygem/igeshandler.py @@ -2,12 +2,14 @@ Derived module from filehandler.py to handle iges and igs files. """ -from OCC.IGESControl import (IGESControl_Reader, IGESControl_Writer, IGESControl_Controller_Init) +from OCC.IGESControl import (IGESControl_Reader, IGESControl_Writer, + IGESControl_Controller_Init) +from OCC.IFSelect import IFSelect_RetDone from pygem.nurbshandler import NurbsHandler class IgesHandler(NurbsHandler): - """ + """ Iges file handler class :cvar string infile: name of the input file to be processed. @@ -28,12 +30,12 @@ class IgesHandler(NurbsHandler): made for analysis but for design purposes. """ - def __init__(self): - super(IgesHandler, self).__init__() - self.extensions = ['.iges', '.igs'] + def __init__(self): + super(IgesHandler, self).__init__() + self.extensions = ['.iges', '.igs'] - def load_shape_from_file(self, filename): - """ + def load_shape_from_file(self, filename): + """ This class method loads a shape from the file `filename`. :param string filename: name of the input file. @@ -42,25 +44,30 @@ def load_shape_from_file(self, filename): :return: shape: loaded shape :rtype: TopoDS_Shape """ - self._check_filename_type(filename) - self._check_extension(filename) - reader = IGESControl_Reader() - reader.ReadFile(filename) - reader.TransferRoots() - shape = reader.Shape() - return shape + self._check_filename_type(filename) + self._check_extension(filename) + reader = IGESControl_Reader() + return_reader = reader.ReadFile(filename) + # check status + if return_reader == IFSelect_RetDone: + return_transfer = reader.TransferRoots() + if return_transfer: + # load all shapes in one + shape = reader.OneShape() - def write_shape_to_file(self, shape, filename): - """ + return shape + + def write_shape_to_file(self, shape, filename): + """ This class method saves the `shape` to the file `filename`. :param: TopoDS_Shape shape: loaded shape :param string filename: name of the input file. It should have proper extension (.iges or .igs) """ - self._check_filename_type(filename) - self._check_extension(filename) - IGESControl_Controller_Init() - writer = IGESControl_Writer() - writer.AddShape(shape) - writer.Write(filename) + self._check_filename_type(filename) + self._check_extension(filename) + IGESControl_Controller_Init() + writer = IGESControl_Writer() + writer.AddShape(shape) + writer.Write(filename) From 1b29033ca9360f01a01dc41b07aa737a41232396 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Tue, 2 May 2017 16:53:32 +0200 Subject: [PATCH 2/9] Fix step read shape --- pygem/stephandler.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/pygem/stephandler.py b/pygem/stephandler.py index 7b45096..11c4355 100644 --- a/pygem/stephandler.py +++ b/pygem/stephandler.py @@ -4,6 +4,7 @@ from OCC.Interface import Interface_Static_SetCVal from OCC.STEPControl import STEPControl_Writer, STEPControl_Reader, STEPControl_AsIs +from OCC.IFSelect import IFSelect_RetDone from pygem.nurbshandler import NurbsHandler @@ -48,9 +49,14 @@ def load_shape_from_file(self, filename): self._check_filename_type(filename) self._check_extension(filename) reader = STEPControl_Reader() - reader.ReadFile(filename) - reader.TransferRoots() - shape = reader.Shape() + return_reader = reader.ReadFile(filename) + # check status + if return_reader == IFSelect_RetDone: + return_transfer = reader.TransferRoots() + if return_transfer: + # load all shapes in one + shape = reader.OneShape() + return shape def write_shape_to_file(self, shape, filename): From cc3cb69a3cc89d1fe019298f5a1fe4d4b95cb631 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Wed, 3 May 2017 16:34:00 +0200 Subject: [PATCH 3/9] Add parse* and write* functions in nurbshandler --- pygem/nurbshandler.py | 452 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 418 insertions(+), 34 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index cdc991a..8f799c9 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -7,15 +7,24 @@ import numpy as np import OCC.TopoDS from OCC.BRep import (BRep_Tool, BRep_Builder) -from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace, \ - BRepBuilderAPI_NurbsConvert, BRepBuilderAPI_MakeWire) +from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, + BRepBuilderAPI_MakeFace, \ + BRepBuilderAPI_NurbsConvert, + BRepBuilderAPI_MakeWire, + BRepBuilderAPI_Sewing) from OCC.Display.SimpleGui import init_display from OCC.GeomConvert import geomconvert_SurfaceToBSplineSurface -from OCC.ShapeFix import ShapeFix_ShapeTolerance +from OCC.GeomConvert import geomconvert_CurveToBSplineCurve +from OCC.ShapeFix import (ShapeFix_ShapeTolerance, ShapeFix_Shell) +from OCC.ShapeAnalysis import ShapeAnalysis_WireOrder from OCC.StlAPI import StlAPI_Writer -from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE) -from OCC.TopExp import TopExp_Explorer +from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE, + TopAbs_FORWARD, TopAbs_SHELL) +from OCC.TopExp import (TopExp_Explorer, topexp) from OCC.gp import (gp_Pnt, gp_XYZ) +from OCC.BRep import * +from OCC.TColgp import (TColgp_Array1OfPnt, TColgp_Array2OfPnt) +from OCC.BRepOffsetAPI import * from matplotlib import pyplot from mpl_toolkits import mplot3d from stl import mesh @@ -56,7 +65,8 @@ def _check_infile_instantiation(self): """ if not self.shape or not self.infile: - raise RuntimeError("You can not write a file without having parsed one.") + raise RuntimeError( + "You can not write a file without having parsed one.") def load_shape_from_file(self, filename): """ @@ -64,8 +74,9 @@ def load_shape_from_file(self, filename): Not implemented, it has to be implemented in subclasses. """ - raise NotImplementedError("Subclass must implement abstract method " +\ - self.__class__.__name__ + ".load_shape_from_file") + raise NotImplementedError("Subclass must implement abstract method " + \ + self.__class__.__name__ + ".load_shape_from_file") + def parse(self, filename): """ @@ -79,7 +90,6 @@ def parse(self, filename): """ self.infile = filename - self.shape = self.load_shape_from_file(filename) # cycle on the faces to get the control points @@ -104,22 +114,25 @@ def parse(self, filename): # extract the Control Points of each face n_poles_u = occ_face.NbUPoles() n_poles_v = occ_face.NbVPoles() - control_polygon_coordinates = np.zeros(\ + control_polygon_coordinates = np.zeros( \ shape=(n_poles_u * n_poles_v, 3)) # cycle over the poles to get their coordinates i = 0 for pole_u_direction in xrange(n_poles_u): for pole_v_direction in xrange(n_poles_v): - control_point_coordinates = occ_face.Pole(\ + control_point_coordinates = occ_face.Pole( \ pole_u_direction + 1, pole_v_direction + 1) - control_polygon_coordinates[i, :] = [control_point_coordinates.X(),\ - control_point_coordinates.Y(),\ + control_polygon_coordinates[i, :] = [ + control_point_coordinates.X(), \ + control_point_coordinates.Y(), \ control_point_coordinates.Z()] i += 1 # pushing the control points coordinates to the mesh_points array (used for FFD) - mesh_points = np.append(mesh_points, control_polygon_coordinates, axis=0) - control_point_position.append(control_point_position[-1] + n_poles_u * n_poles_v) + mesh_points = np.append(mesh_points, control_polygon_coordinates, + axis=0) + control_point_position.append( + control_point_position[-1] + n_poles_u * n_poles_v) n_faces += 1 faces_explorer.Next() @@ -174,17 +187,21 @@ def write(self, mesh_points, filename, tolerance=None): i = 0 for pole_u_direction in xrange(n_poles_u): for pole_v_direction in xrange(n_poles_v): - control_point_coordinates = mesh_points[i + control_point_position[n_faces], :] + control_point_coordinates = mesh_points[ + i + control_point_position[ + n_faces], :] point_xyz = gp_XYZ(*control_point_coordinates) gp_point = gp_Pnt(point_xyz) - occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, gp_point) + occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, + gp_point) i += 1 # construct the deformed wire for the trimmed surfaces wire_maker = BRepBuilderAPI_MakeWire() tol = ShapeFix_ShapeTolerance() - brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), self.tolerance).Face() + brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + self.tolerance).Face() brep_face = BRep_Tool.Surface(brep) # cycle on the edges @@ -194,7 +211,7 @@ def write(self, mesh_points, filename, tolerance=None): # edge in the (u,v) coordinates edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones - edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge(\ + edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge( \ edge_uv_coordinates[0], brep_face) edge_phis_coordinates = edge_phis_coordinates_aux.Edge() tol.SetTolerance(edge_phis_coordinates, self.tolerance) @@ -205,12 +222,378 @@ def write(self, mesh_points, filename, tolerance=None): wire = wire_maker.Wire() # trimming the surfaces - brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), wire).Shape() + brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + wire).Shape() compound_builder.Add(compound, brep_surf) n_faces += 1 faces_explorer.Next() self.write_shape_to_file(compound, self.outfile) + + def parse_face(self, topo_face): + """ + Method to parse a single Face (a single patch nurbs surface). + It returns a matrix with all the coordinates of control points of the + Face and a second list with all the control points related to the + Edges of the Face. + + :param topo_face: the input Face + + :return: mesh_points_face: it is a `n_points`-by-3 matrix containing the + coordinates of the control points of the Face (a nurbs surface) + + :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix + + :rtype: numpy.ndarray and list + + """ + # get some Face - Edge - Vertex data map information + mesh_points_edge = [] + face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) + # loop on wires per face + while face_exp_wire.More(): + twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) + wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) + # loop on edges per wire + while wire_exp_edge.More(): + edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) + bspline_converter = BRepBuilderAPI_NurbsConvert(edge) + bspline_converter.Perform(edge) + bspline_tshape_edge = bspline_converter.Shape() + h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( + bspline_tshape_edge)) + h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) + bspline_geom_edge = h_bspline_edge.GetObject() + + nb_poles = bspline_geom_edge.NbPoles() + + # Edge geometric properties + edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) + bspline_geom_edge.Poles(edge_ctrlpts) + + points_single_edge = np.zeros((0, 3)) + for i in range(1, nb_poles + 1): + ctrlpt = edge_ctrlpts.Value(i) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + points_single_edge = np.append(points_single_edge, + ctrlpt_position, + axis=0) + + mesh_points_edge.append(points_single_edge) + + wire_exp_edge.Next() + + face_exp_wire.Next() + # extract mesh points (control points) on Face + mesh_points_face = np.zeros((0, 3)) + # convert Face to Geom B-spline Face + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + # get access to control points (poles) + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) + bsurface.Poles(ctrlpts) + + for indice_u_direction in range(1, nb_u + 1): + for indice_v_direction in range(1, nb_v + 1): + ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + mesh_points_face = np.append(mesh_points_face, + ctrlpt_position, axis=0) + + return {'F': mesh_points_face, 'E': mesh_points_edge} + + def parse_shape(self, filename): + """ + Method to parse a Shape with multiple Faces. + It returns a list of matrix with all the coordinates of control points + of each Face and a second list with all the control points related to + Edges of each Face. + + :param filename: the input filename. + + :return: mesh_points: it is a list of `n_points`-by-3 matrix containing + the coordinates of the control points of the Shape (surface) + edge_points: it is a list + :rtype: numpy.ndarray, list + + """ + self.infile = filename + self.shape = self.load_shape_from_file(filename) + + # parse and get control points + shape_parse_face = TopExp_Explorer(self.shape, TopAbs_FACE) + mesh_points = [] # a Python list + edge_points = [] + + while shape_parse_face.More(): + topo_face = OCC.TopoDS.topods.Face(shape_parse_face.Current()) + get_parse_face = self.parse_face(topo_face) + mesh_point_face = get_parse_face.get('F') + edge_point = get_parse_face.get('E') + mesh_points.append(mesh_point_face) + edge_points.append(edge_point) + + shape_parse_face.Next() + + return {'F': mesh_points, 'E': edge_points} + + def write_edge(self, points_edge, topo_edge): + """ + Method to recreate an Edge associated to a geometric curve + after the modification of its points. + :param ct_points_edge: the deformed points array. + :param topo_edge: the Edge to be modified + :return: Edge (Shape) + + :rtype: TopoDS_Edge + + """ + # convert Edge to Geom B-spline Curve + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) + nurbs_converter.Perform(topo_edge) + nurbs_curve = nurbs_converter.Shape() + topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) + h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) + h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) + bspline_edge_curve = h_bcurve.GetObject() + + # Edge geometric properties + nb_cpt = bspline_edge_curve.NbPoles() + indice_cpt = 0 + if points_edge.shape[0] != nb_cpt: + print "Number of edge points not equal! " + return 0 + else: + for i in range(1, nb_cpt + 1): + cpt = points_edge[indice_cpt] + bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) + indice_cpt += 1 + + new_bspline_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) + return new_bspline_edge.Edge() + + def write_face(self, points_face, list_points_edge, topo_face, toledge): + """ + Method to recreate a Face associated to a geometric surface + after the modification of Face points. It returns a TopoDS_Face. + + :param points_face: the new face points array. + :param topo_face: the face to be modified + :param toledge: tolerance on the surface creation after modification + :return: TopoDS_Face (Shape) + + :rtype: TopoDS_Shape + + """ + + # convert Face to Geom B-spline Surface + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) + h_geomsurface = BRep_Tool.Surface(topo_nurbsface) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + # check consistency + if points_face.shape[0] != nb_u * nb_v: + print "Number of face points not equal! " + return 0 + + # cycle on the face points + indice_cpt = 0 + for iu in range(1, nb_u + 1): + for iv in range(1, nb_v + 1): + cpt = points_face[indice_cpt] + bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) + indice_cpt += 1 + + # create modified new face + new_bspline_tface = BRepBuilderAPI_MakeFace() + toler = OCC.Precision.precision_Confusion() + new_bspline_tface.Init(bsurface.GetHandle(), False, toler) + + # cycle on the wires + face_explorer_wire = TopExp_Explorer(topo_nurbsface.Oriented(TopAbs_FORWARD), + TopAbs_WIRE) + ind_edge_total = 0 + + while face_explorer_wire.More(): + # get old wire + twire = OCC.TopoDS.topods_Wire(face_explorer_wire.Current()) + + # cycle on the edges + ind_edge = 0 + wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), + TopAbs_EDGE) + # check edges order on the wire + mode3d = True + tolerance_edges = toledge + + wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) + # an edge list + deformed_edges = [] + # cycle on the edges + while wire_explorer_edge.More(): + tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) + new_bspline_tedge = self.write_edge(list_points_edge[ind_edge_total], + tedge) + + deformed_edges.append(new_bspline_tedge) + analyzer = topexp() + vfirst = analyzer.FirstVertex(new_bspline_tedge) + vlast = analyzer.LastVertex(new_bspline_tedge) + pt1 = BRep_Tool.Pnt(vfirst) + pt2 = BRep_Tool.Pnt(vlast) + + wire_order.Add(pt1.XYZ(), pt2.XYZ()) + + ind_edge += 1 + ind_edge_total += 1 + wire_explorer_edge.Next() + + # grouping the edges in a wire, then in the face + # check edges order and connectivity within the wire + wire_order.Perform() + # new wire to be created + stol = ShapeFix_ShapeTolerance() + new_bspline_twire = BRepBuilderAPI_MakeWire() + for order_i in range(1, wire_order.NbEdges() + 1): + deformed_edge_i = wire_order.Ordered(order_i) + if deformed_edge_i > 0: + # insert the deformed edge to the new wire + new_edge_toadd = deformed_edges[deformed_edge_i - 1] + stol.SetTolerance(new_edge_toadd, toledge) + new_bspline_twire.Add(new_edge_toadd) + if new_bspline_twire.Error() != 0: + print "Wire Add Error: " + str(new_bspline_twire.Error()) + stol.SetTolerance(new_edge_toadd, toledge * 10.0) + new_bspline_twire.Add(new_edge_toadd) + else: + deformed_edge_revers = deformed_edges[np.abs(deformed_edge_i) - 1] + stol.SetTolerance(deformed_edge_revers, toledge) + new_bspline_twire.Add(deformed_edge_revers) + if new_bspline_twire.Error() != 0: + print "Rever Add Error: " + str(new_bspline_twire.Error()) + stol.SetTolerance(deformed_edge_revers, toledge * 10.0) + new_bspline_twire.Add(deformed_edge_revers) + # add new wire to the Face + new_bspline_tface.Add(new_bspline_twire.Wire()) + face_explorer_wire.Next() + + return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) + + def combine_faces(self, compshape, sew_tolerance): + """ + Method to combine faces in a shell by adding connectivity and continuity + :param compshape: TopoDS_Shape + :param sew_tolerance: tolerance for sewing + :return: Topo_Shell + """ + + offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) + sew = BRepBuilderAPI_Sewing(sew_tolerance) + + shape_exp_face = TopExp_Explorer(compshape, TopAbs_FACE) + nb_faces = 0 + # cycle on Faces + while shape_exp_face.More(): + tface = OCC.TopoDS.topods.Face(shape_exp_face.Current()) + sew.Add(tface) + offsew.Add(tface) + nb_faces += 1 + shape_exp_face.Next() + + offsew.Perform() + offsew.Dump() + sew.Perform() + shell = sew.SewedShape() + sew.Dump() + + shell = OCC.TopoDS.topods.Shell(shell) + shell_fixer = ShapeFix_Shell() + shell_fixer.FixFaceOrientation(shell) + + if shell_fixer.Perform(): + print str(shell_fixer.NbShells()) + " shells fixed! " + else: + print str(shell_fixer.NbShells()) + " shells failed! " + + new_shell = shell_fixer.Shell() + + if OCC.BRepAlgo.brepalgo_IsValid(new_shell): + print "Shell valid! " + else: + print "Shell failed! " + return new_shell + + def write_shape(self, face_points, list_edge_points, filename, tol): + """ + Method to recreate a TopoDS_Shape associated to a geometric shape + after the modification of points of each Face. It + returns a TopoDS_Shape (Shape). + + :param face_points: the new face points array + :param list_edge_points: the new edge points list + :param filename: the output filename + :param tol: tolerance on the surface creation after modification + :return: TopoDS_Compound + + :rtype: TopoDS_Compound + + """ + self.outfile = filename + + global_compound_builder = BRep_Builder() + global_comp = OCC.TopoDS.TopoDS_Compound() + global_compound_builder.MakeCompound(global_comp) + # cycle on shells (multiple objects) + shape_exp_shell = TopExp_Explorer(self.shape.Oriented(TopAbs_FORWARD), + TopAbs_SHELL) + ishell = 0 + iface = 0 + while shape_exp_shell.More(): + per_shell = OCC.TopoDS.topods_Shell(shape_exp_shell.Current()) + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + # cycle on faces + shape_exp_face = TopExp_Explorer(per_shell.Oriented(TopAbs_FORWARD), + TopAbs_FACE) + + while shape_exp_face.More(): + cpts = face_points[iface] + edge_cpts = list_edge_points[iface] + tpface = OCC.TopoDS.topods.Face(shape_exp_face.Current()) + tdface = self.write_face(cpts, edge_cpts, tpface, tol) + + # add face to compound + compound_builder.Add(comp, tdface) + iface += 1 + shape_exp_face.Next() + + new_shell = self.combine_faces(comp, 0.01) + global_compound_builder.Add(global_comp, new_shell) + + + ishell += 1 + shape_exp_shell.Next() + + self.write_shape_to_file(global_comp, self.outfile) + def write_shape_to_file(self, shape, filename): """ Abstract method to write the 'shape' to the `filename`. @@ -218,7 +601,7 @@ def write_shape_to_file(self, shape, filename): Not implemented, it has to be implemented in subclasses. """ raise NotImplementedError(\ - "Subclass must implement abstract method " +\ + "Subclass must implement abstract method " + \ self.__class__.__name__ + ".write_shape_to_file") def plot(self, plot_file=None, save_fig=False): @@ -250,27 +633,28 @@ def plot(self, plot_file=None, save_fig=False): # Load the STL files and add the vectors to the plot stl_mesh = mesh.Mesh.from_file('aux_figure.stl') os.remove('aux_figure.stl') - axes.add_collection3d(mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) + axes.add_collection3d( + mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) # Get the limits of the axis and center the geometry - max_dim = np.array([\ - np.max(stl_mesh.vectors[:, :, 0]) / 1000,\ - np.max(stl_mesh.vectors[:, :, 1]) / 1000,\ + max_dim = np.array([ \ + np.max(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.max(stl_mesh.vectors[:, :, 1]) / 1000, \ np.max(stl_mesh.vectors[:, :, 2]) / 1000]) - min_dim = np.array([\ - np.min(stl_mesh.vectors[:, :, 0]) / 1000,\ - np.min(stl_mesh.vectors[:, :, 1]) / 1000,\ + min_dim = np.array([ \ + np.min(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.min(stl_mesh.vectors[:, :, 1]) / 1000, \ np.min(stl_mesh.vectors[:, :, 2]) / 1000]) max_lenght = np.max(max_dim - min_dim) - axes.set_xlim(\ - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2,\ + axes.set_xlim( \ + -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, \ .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) - axes.set_ylim(\ - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2,\ + axes.set_ylim( \ + -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, \ .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) - axes.set_zlim(\ - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2,\ + axes.set_zlim( \ + -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, \ .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) # Show the plot to the screen From f4c2f565bce190c6ea1b72ff81acc633ead4a8a8 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Fri, 19 May 2017 14:52:11 +0200 Subject: [PATCH 4/9] Avoid import using * --- pygem/nurbshandler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index 8f799c9..f5c6611 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -6,7 +6,7 @@ import os import numpy as np import OCC.TopoDS -from OCC.BRep import (BRep_Tool, BRep_Builder) +from OCC.BRep import (BRep_Tool, BRep_Builder, BRep_Tool_Curve) from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, BRepBuilderAPI_MakeFace, \ BRepBuilderAPI_NurbsConvert, @@ -22,9 +22,8 @@ TopAbs_FORWARD, TopAbs_SHELL) from OCC.TopExp import (TopExp_Explorer, topexp) from OCC.gp import (gp_Pnt, gp_XYZ) -from OCC.BRep import * from OCC.TColgp import (TColgp_Array1OfPnt, TColgp_Array2OfPnt) -from OCC.BRepOffsetAPI import * +from OCC.BRepOffsetAPI import BRepOffsetAPI_FindContigousEdges from matplotlib import pyplot from mpl_toolkits import mplot3d from stl import mesh From 7d48a32dc46dd8d111be0d16862032b596af6a66 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Fri, 19 May 2017 15:02:34 +0200 Subject: [PATCH 5/9] Check static methods and return values --- pygem/nurbshandler.py | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index f5c6611..c8c255a 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -228,8 +228,8 @@ def write(self, mesh_points, filename, tolerance=None): faces_explorer.Next() self.write_shape_to_file(compound, self.outfile) - - def parse_face(self, topo_face): + @staticmethod + def parse_face(topo_face): """ Method to parse a single Face (a single patch nurbs surface). It returns a matrix with all the coordinates of control points of the @@ -243,7 +243,7 @@ def parse_face(self, topo_face): :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix - :rtype: numpy.ndarray and list + :rtype: (numpy.ndarray, list) """ # get some Face - Edge - Vertex data map information @@ -310,7 +310,7 @@ def parse_face(self, topo_face): mesh_points_face = np.append(mesh_points_face, ctrlpt_position, axis=0) - return {'F': mesh_points_face, 'E': mesh_points_edge} + return mesh_points_face, mesh_points_edge def parse_shape(self, filename): """ @@ -324,7 +324,7 @@ def parse_shape(self, filename): :return: mesh_points: it is a list of `n_points`-by-3 matrix containing the coordinates of the control points of the Shape (surface) edge_points: it is a list - :rtype: numpy.ndarray, list + :rtype: (list, list) """ self.infile = filename @@ -337,21 +337,20 @@ def parse_shape(self, filename): while shape_parse_face.More(): topo_face = OCC.TopoDS.topods.Face(shape_parse_face.Current()) - get_parse_face = self.parse_face(topo_face) - mesh_point_face = get_parse_face.get('F') - edge_point = get_parse_face.get('E') + mesh_point_face, edge_point = self.parse_face(topo_face) mesh_points.append(mesh_point_face) edge_points.append(edge_point) shape_parse_face.Next() - return {'F': mesh_points, 'E': edge_points} + return mesh_points, edge_points - def write_edge(self, points_edge, topo_edge): + @staticmethod + def write_edge(points_edge, topo_edge): """ Method to recreate an Edge associated to a geometric curve after the modification of its points. - :param ct_points_edge: the deformed points array. + :param points_edge: the deformed points array. :param topo_edge: the Edge to be modified :return: Edge (Shape) @@ -388,6 +387,7 @@ def write_face(self, points_face, list_points_edge, topo_face, toledge): after the modification of Face points. It returns a TopoDS_Face. :param points_face: the new face points array. + :param list_points_edge: new edge points :param topo_face: the face to be modified :param toledge: tolerance on the surface creation after modification :return: TopoDS_Face (Shape) @@ -495,7 +495,8 @@ def write_face(self, points_face, list_points_edge, topo_face, toledge): return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) - def combine_faces(self, compshape, sew_tolerance): + @staticmethod + def combine_faces(compshape, sew_tolerance): """ Method to combine faces in a shell by adding connectivity and continuity :param compshape: TopoDS_Shape From a2bf84f6170b0622cebf87428cd499fc313f3901 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Tue, 13 Jun 2017 17:28:47 +0200 Subject: [PATCH 6/9] Update nurbshandler.py by adding a list of shells --- pygem/nurbshandler.py | 1328 +++++++++++++++++++++-------------------- 1 file changed, 673 insertions(+), 655 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index 4656061..49d434b 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -8,10 +8,10 @@ import OCC.TopoDS from OCC.BRep import (BRep_Tool, BRep_Builder, BRep_Tool_Curve) from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeFace, \ + BRepBuilderAPI_MakeFace, BRepBuilderAPI_NurbsConvert, BRepBuilderAPI_MakeWire, - BRepBuilderAPI_Sewing) + BRepBuilderAPI_Sewing) from OCC.Display.SimpleGui import init_display from OCC.GeomConvert import geomconvert_SurfaceToBSplineSurface from OCC.GeomConvert import geomconvert_CurveToBSplineCurve @@ -19,7 +19,7 @@ from OCC.ShapeAnalysis import ShapeAnalysis_WireOrder from OCC.StlAPI import StlAPI_Writer from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE, - TopAbs_FORWARD, TopAbs_SHELL) + TopAbs_FORWARD, TopAbs_SHELL) from OCC.TopExp import (TopExp_Explorer, topexp) from OCC.gp import (gp_Pnt, gp_XYZ) from OCC.TColgp import (TColgp_Array1OfPnt, TColgp_Array2OfPnt) @@ -31,657 +31,675 @@ class NurbsHandler(fh.FileHandler): - """ - Nurbs file handler base class - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list control_point_position: index of the first NURBS control point (or pole) - of each face of the files. - :cvar TopoDS_Shape shape: shape meant for modification. - :cvar float tolerance: tolerance for the construction of the faces and wires - in the write function. Default value is 1e-6. - - .. warning:: - - - For non trivial geometries it could be necessary to increase the tolerance. - Linking edges into a single wire and then trimming the surface with the wire - can be hard for the software, especially when the starting CAD has not been - made for analysis but for design purposes. - """ - - def __init__(self): - super(NurbsHandler, self).__init__() - self._control_point_position = None - self.tolerance = 1e-6 - self.shape = None - - def _check_infile_instantiation(self): - """ - This private method checks if `self.infile` and `self.shape' are instantiated. If not it means - that nobody called the parse method and at least one of them is None` If the check fails - it raises a RuntimeError. - - """ - if not self.shape or not self.infile: - raise RuntimeError( - "You can not write a file without having parsed one.") - - def load_shape_from_file(self, filename): - """ - Abstract method to load a specific file as a shape. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError("Subclass must implement abstract method " + \ - self.__class__.__name__ + ".load_shape_from_file") - - - def parse(self, filename): - """ - Method to parse the file `filename`. It returns a matrix with all the coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh - :rtype: numpy.ndarray - - """ - self.infile = filename - self.shape = self.load_shape_from_file(filename) - - # cycle on the faces to get the control points - # init some quantities - n_faces = 0 - control_point_position = [0] - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - mesh_points = np.zeros(shape=(0, 3)) - - while faces_explorer.More(): - # performing some conversions to get the right format (BSplineSurface) - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - - # openCascade object - occ_face = bspline_face.GetObject() - - # extract the Control Points of each face - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - control_polygon_coordinates = np.zeros( \ - shape=(n_poles_u * n_poles_v, 3)) - - # cycle over the poles to get their coordinates - i = 0 - - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = occ_face.Pole(\ - - pole_u_direction + 1, pole_v_direction + 1) - control_polygon_coordinates[i, :] = [ - control_point_coordinates.X(), \ - control_point_coordinates.Y(), \ - control_point_coordinates.Z()] - i += 1 - # pushing the control points coordinates to the mesh_points array (used for FFD) - mesh_points = np.append(mesh_points, control_polygon_coordinates, - axis=0) - control_point_position.append( - control_point_position[-1] + n_poles_u * n_poles_v) - - n_faces += 1 - faces_explorer.Next() - self._control_point_position = control_point_position - return mesh_points - - def write(self, mesh_points, filename, tolerance=None): - """ - Writes a output file, called filename, copying all the structures from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the output file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh - :param string filename: name of the output file. - :param float tolerance: tolerance for the construction of the faces and wires - in the write function. If not given it uses `self.tolerance`. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - if tolerance is not None: - self.tolerance = tolerance - - # cycle on the faces to update the control points position - # init some quantities - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - n_faces = 0 - control_point_position = self._control_point_position - - compound_builder = BRep_Builder() - compound = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(compound) - - while faces_explorer.More(): - # similar to the parser method - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - face_aux = OCC.TopoDS.topods_Face(nurbs_face) - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - occ_face = bspline_face.GetObject() - - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - - i = 0 - - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = ( + """ + Nurbs file handler base class + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list control_point_position: index of the first NURBS control point (or pole) + of each face of the files. + :cvar TopoDS_Shape shape: shape meant for modification. + :cvar float tolerance: tolerance for the construction of the faces and wires + in the write function. Default value is 1e-6. + + .. warning:: + + - For non trivial geometries it could be necessary to increase the tolerance. + Linking edges into a single wire and then trimming the surface with the wire + can be hard for the software, especially when the starting CAD has not been + made for analysis but for design purposes. + """ + + def __init__(self): + super(NurbsHandler, self).__init__() + self._control_point_position = None + self.tolerance = 1e-6 + self.shape = None + + def _check_infile_instantiation(self): + """ + This private method checks if `self.infile` and `self.shape' are instantiated. If not it means + that nobody called the parse method and at least one of them is None` If the check fails + it raises a RuntimeError. + + """ + if not self.shape or not self.infile: + raise RuntimeError( + "You can not write a file without having parsed one.") + + def load_shape_from_file(self, filename): + """ + Abstract method to load a specific file as a shape. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError("Subclass must implement abstract method " + \ + self.__class__.__name__ + ".load_shape_from_file") + + + def parse(self, filename): + """ + Method to parse the file `filename`. It returns a matrix with all the coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh + :rtype: numpy.ndarray + + """ + self.infile = filename + self.shape = self.load_shape_from_file(filename) + + # cycle on the faces to get the control points + # init some quantities + n_faces = 0 + control_point_position = [0] + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + mesh_points = np.zeros(shape=(0, 3)) + + while faces_explorer.More(): + # performing some conversions to get the right format (BSplineSurface) + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + + # openCascade object + occ_face = bspline_face.GetObject() + + # extract the Control Points of each face + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + control_polygon_coordinates = np.zeros( \ + shape=(n_poles_u * n_poles_v, 3)) + + # cycle over the poles to get their coordinates + i = 0 + + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = occ_face.Pole(\ + + pole_u_direction + 1, pole_v_direction + 1) + control_polygon_coordinates[i, :] = [ + control_point_coordinates.X(), \ + control_point_coordinates.Y(), \ + control_point_coordinates.Z()] + i += 1 + # pushing the control points coordinates to the mesh_points array (used for FFD) + mesh_points = np.append(mesh_points, control_polygon_coordinates, + axis=0) + control_point_position.append( + control_point_position[-1] + n_poles_u * n_poles_v) + + n_faces += 1 + faces_explorer.Next() + self._control_point_position = control_point_position + return mesh_points + + def write(self, mesh_points, filename, tolerance=None): + """ + Writes a output file, called filename, copying all the structures from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the output file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh + :param string filename: name of the output file. + :param float tolerance: tolerance for the construction of the faces and wires + in the write function. If not given it uses `self.tolerance`. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + if tolerance is not None: + self.tolerance = tolerance + + # cycle on the faces to update the control points position + # init some quantities + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + control_point_position = self._control_point_position + + compound_builder = BRep_Builder() + compound = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(compound) + + while faces_explorer.More(): + # similar to the parser method + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + face_aux = OCC.TopoDS.topods_Face(nurbs_face) + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + occ_face = bspline_face.GetObject() + + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + + i = 0 + + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = ( mesh_points[i + control_point_position[n_faces], :] ) - point_xyz = gp_XYZ(*control_point_coordinates) - - gp_point = gp_Pnt(point_xyz) - occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, - gp_point) - i += 1 - - # construct the deformed wire for the trimmed surfaces - wire_maker = BRepBuilderAPI_MakeWire() - tol = ShapeFix_ShapeTolerance() - brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), - self.tolerance).Face() - brep_face = BRep_Tool.Surface(brep) - - # cycle on the edges - edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) - while edge_explorer.More(): - edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) - # edge in the (u,v) coordinates - edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) - # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones - edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge( \ - edge_uv_coordinates[0], brep_face) - edge_phis_coordinates = edge_phis_coordinates_aux.Edge() - tol.SetTolerance(edge_phis_coordinates, self.tolerance) - wire_maker.Add(edge_phis_coordinates) - edge_explorer.Next() - - # grouping the edges in a wire - wire = wire_maker.Wire() - - # trimming the surfaces - brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), - wire).Shape() - compound_builder.Add(compound, brep_surf) - n_faces += 1 - faces_explorer.Next() - self.write_shape_to_file(compound, self.outfile) - - @staticmethod - def parse_face(topo_face): - """ - Method to parse a single Face (a single patch nurbs surface). - It returns a matrix with all the coordinates of control points of the - Face and a second list with all the control points related to the - Edges of the Face. - - :param topo_face: the input Face - - :return: mesh_points_face: it is a `n_points`-by-3 matrix containing the - coordinates of the control points of the Face (a nurbs surface) - - :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix - - :rtype: (numpy.ndarray, list) - - """ - # get some Face - Edge - Vertex data map information - mesh_points_edge = [] - face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) - # loop on wires per face - while face_exp_wire.More(): - twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) - wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) - # loop on edges per wire - while wire_exp_edge.More(): - edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) - bspline_converter = BRepBuilderAPI_NurbsConvert(edge) - bspline_converter.Perform(edge) - bspline_tshape_edge = bspline_converter.Shape() - h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( - bspline_tshape_edge)) - h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) - bspline_geom_edge = h_bspline_edge.GetObject() - - nb_poles = bspline_geom_edge.NbPoles() - - # Edge geometric properties - edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) - bspline_geom_edge.Poles(edge_ctrlpts) - - points_single_edge = np.zeros((0, 3)) - for i in range(1, nb_poles + 1): - ctrlpt = edge_ctrlpts.Value(i) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - points_single_edge = np.append(points_single_edge, - ctrlpt_position, - axis=0) - - mesh_points_edge.append(points_single_edge) - - wire_exp_edge.Next() - - face_exp_wire.Next() - # extract mesh points (control points) on Face - mesh_points_face = np.zeros((0, 3)) - # convert Face to Geom B-spline Face - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - # get access to control points (poles) - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) - bsurface.Poles(ctrlpts) - - for indice_u_direction in range(1, nb_u + 1): - for indice_v_direction in range(1, nb_v + 1): - ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - mesh_points_face = np.append(mesh_points_face, - ctrlpt_position, axis=0) - - return mesh_points_face, mesh_points_edge - - def parse_shape(self, filename): - """ - Method to parse a Shape with multiple Faces. - It returns a list of matrix with all the coordinates of control points - of each Face and a second list with all the control points related to - Edges of each Face. - - :param filename: the input filename. - - :return: mesh_points: it is a list of `n_points`-by-3 matrix containing - the coordinates of the control points of the Shape (surface) - edge_points: it is a list - :rtype: (list, list) - - """ - self.infile = filename - self.shape = self.load_shape_from_file(filename) - - # parse and get control points - shape_parse_face = TopExp_Explorer(self.shape, TopAbs_FACE) - mesh_points = [] # a Python list - edge_points = [] - - while shape_parse_face.More(): - topo_face = OCC.TopoDS.topods.Face(shape_parse_face.Current()) - mesh_point_face, edge_point = self.parse_face(topo_face) - mesh_points.append(mesh_point_face) - edge_points.append(edge_point) - - shape_parse_face.Next() - - return mesh_points, edge_points - - @staticmethod - def write_edge(points_edge, topo_edge): - """ - Method to recreate an Edge associated to a geometric curve - after the modification of its points. - :param points_edge: the deformed points array. - :param topo_edge: the Edge to be modified - :return: Edge (Shape) - - :rtype: TopoDS_Edge - - """ - # convert Edge to Geom B-spline Curve - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) - nurbs_converter.Perform(topo_edge) - nurbs_curve = nurbs_converter.Shape() - topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) - h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) - h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) - bspline_edge_curve = h_bcurve.GetObject() - - # Edge geometric properties - nb_cpt = bspline_edge_curve.NbPoles() - indice_cpt = 0 - if points_edge.shape[0] != nb_cpt: - print "Number of edge points not equal! " - return 0 - else: - for i in range(1, nb_cpt + 1): - cpt = points_edge[indice_cpt] - bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) - indice_cpt += 1 - - new_bspline_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) - return new_bspline_edge.Edge() - - def write_face(self, points_face, list_points_edge, topo_face, toledge): - """ - Method to recreate a Face associated to a geometric surface - after the modification of Face points. It returns a TopoDS_Face. - - :param points_face: the new face points array. - :param list_points_edge: new edge points - :param topo_face: the face to be modified - :param toledge: tolerance on the surface creation after modification - :return: TopoDS_Face (Shape) - - :rtype: TopoDS_Shape - - """ - - # convert Face to Geom B-spline Surface - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) - h_geomsurface = BRep_Tool.Surface(topo_nurbsface) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - # check consistency - if points_face.shape[0] != nb_u * nb_v: - print "Number of face points not equal! " - return 0 - - # cycle on the face points - indice_cpt = 0 - for iu in range(1, nb_u + 1): - for iv in range(1, nb_v + 1): - cpt = points_face[indice_cpt] - bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) - indice_cpt += 1 - - # create modified new face - new_bspline_tface = BRepBuilderAPI_MakeFace() - toler = OCC.Precision.precision_Confusion() - new_bspline_tface.Init(bsurface.GetHandle(), False, toler) - - # cycle on the wires - face_explorer_wire = TopExp_Explorer(topo_nurbsface.Oriented(TopAbs_FORWARD), - TopAbs_WIRE) - ind_edge_total = 0 - - while face_explorer_wire.More(): - # get old wire - twire = OCC.TopoDS.topods_Wire(face_explorer_wire.Current()) - - # cycle on the edges - ind_edge = 0 - wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), - TopAbs_EDGE) - # check edges order on the wire - mode3d = True - tolerance_edges = toledge - - wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) - # an edge list - deformed_edges = [] - # cycle on the edges - while wire_explorer_edge.More(): - tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) - new_bspline_tedge = self.write_edge(list_points_edge[ind_edge_total], - tedge) - - deformed_edges.append(new_bspline_tedge) - analyzer = topexp() - vfirst = analyzer.FirstVertex(new_bspline_tedge) - vlast = analyzer.LastVertex(new_bspline_tedge) - pt1 = BRep_Tool.Pnt(vfirst) - pt2 = BRep_Tool.Pnt(vlast) - - wire_order.Add(pt1.XYZ(), pt2.XYZ()) - - ind_edge += 1 - ind_edge_total += 1 - wire_explorer_edge.Next() - - # grouping the edges in a wire, then in the face - # check edges order and connectivity within the wire - wire_order.Perform() - # new wire to be created - stol = ShapeFix_ShapeTolerance() - new_bspline_twire = BRepBuilderAPI_MakeWire() - for order_i in range(1, wire_order.NbEdges() + 1): - deformed_edge_i = wire_order.Ordered(order_i) - if deformed_edge_i > 0: - # insert the deformed edge to the new wire - new_edge_toadd = deformed_edges[deformed_edge_i - 1] - stol.SetTolerance(new_edge_toadd, toledge) - new_bspline_twire.Add(new_edge_toadd) - if new_bspline_twire.Error() != 0: - print "Wire Add Error: " + str(new_bspline_twire.Error()) - stol.SetTolerance(new_edge_toadd, toledge * 10.0) - new_bspline_twire.Add(new_edge_toadd) - else: - deformed_edge_revers = deformed_edges[np.abs(deformed_edge_i) - 1] - stol.SetTolerance(deformed_edge_revers, toledge) - new_bspline_twire.Add(deformed_edge_revers) - if new_bspline_twire.Error() != 0: - print "Rever Add Error: " + str(new_bspline_twire.Error()) - stol.SetTolerance(deformed_edge_revers, toledge * 10.0) - new_bspline_twire.Add(deformed_edge_revers) - # add new wire to the Face - new_bspline_tface.Add(new_bspline_twire.Wire()) - face_explorer_wire.Next() - - return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) - - @staticmethod - def combine_faces(compshape, sew_tolerance): - """ - Method to combine faces in a shell by adding connectivity and continuity - :param compshape: TopoDS_Shape - :param sew_tolerance: tolerance for sewing - :return: Topo_Shell - """ - - offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) - sew = BRepBuilderAPI_Sewing(sew_tolerance) - - shape_exp_face = TopExp_Explorer(compshape, TopAbs_FACE) - nb_faces = 0 - # cycle on Faces - while shape_exp_face.More(): - tface = OCC.TopoDS.topods.Face(shape_exp_face.Current()) - sew.Add(tface) - offsew.Add(tface) - nb_faces += 1 - shape_exp_face.Next() - - offsew.Perform() - offsew.Dump() - sew.Perform() - shell = sew.SewedShape() - sew.Dump() - - shell = OCC.TopoDS.topods.Shell(shell) - shell_fixer = ShapeFix_Shell() - shell_fixer.FixFaceOrientation(shell) - - if shell_fixer.Perform(): - print str(shell_fixer.NbShells()) + " shells fixed! " - else: - print str(shell_fixer.NbShells()) + " shells failed! " - - new_shell = shell_fixer.Shell() - - if OCC.BRepAlgo.brepalgo_IsValid(new_shell): - print "Shell valid! " - else: - print "Shell failed! " - return new_shell - - def write_shape(self, face_points, list_edge_points, filename, tol): - """ - Method to recreate a TopoDS_Shape associated to a geometric shape - after the modification of points of each Face. It - returns a TopoDS_Shape (Shape). - - :param face_points: the new face points array - :param list_edge_points: the new edge points list - :param filename: the output filename - :param tol: tolerance on the surface creation after modification - :return: TopoDS_Compound - - :rtype: TopoDS_Compound - - """ - self.outfile = filename - - global_compound_builder = BRep_Builder() - global_comp = OCC.TopoDS.TopoDS_Compound() - global_compound_builder.MakeCompound(global_comp) - # cycle on shells (multiple objects) - shape_exp_shell = TopExp_Explorer(self.shape.Oriented(TopAbs_FORWARD), - TopAbs_SHELL) - ishell = 0 - iface = 0 - while shape_exp_shell.More(): - per_shell = OCC.TopoDS.topods_Shell(shape_exp_shell.Current()) - compound_builder = BRep_Builder() - comp = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(comp) - # cycle on faces - shape_exp_face = TopExp_Explorer(per_shell.Oriented(TopAbs_FORWARD), - TopAbs_FACE) - - while shape_exp_face.More(): - cpts = face_points[iface] - edge_cpts = list_edge_points[iface] - tpface = OCC.TopoDS.topods.Face(shape_exp_face.Current()) - tdface = self.write_face(cpts, edge_cpts, tpface, tol) - - # add face to compound - compound_builder.Add(comp, tdface) - iface += 1 - shape_exp_face.Next() - - new_shell = self.combine_faces(comp, 0.01) - global_compound_builder.Add(global_comp, new_shell) - - - ishell += 1 - shape_exp_shell.Next() - - self.write_shape_to_file(global_comp, self.outfile) - - def write_shape_to_file(self, shape, filename): - """ - Abstract method to write the 'shape' to the `filename`. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError(\ - "Subclass must implement abstract method " + \ - self.__class__.__name__ + ".write_shape_to_file") - - def plot(self, plot_file=None, save_fig=False): - """ - Method to plot a file. If `plot_file` is not given it plots `self.shape`. - - :param string plot_file: the filename you want to plot. - :param bool save_fig: a flag to save the figure in png or not. If True the - plot is not shown. - - :return: figure: matlplotlib structure for the figure of the chosen geometry - :rtype: matplotlib.pyplot.figure - """ - if plot_file is None: - shape = self.shape - plot_file = self.infile - else: - shape = self.load_shape_from_file(plot_file) - - stl_writer = StlAPI_Writer() - # Do not switch SetASCIIMode() from False to True. - stl_writer.SetASCIIMode(False) - stl_writer.Write(shape, 'aux_figure.stl') - - # Create a new plot - figure = pyplot.figure() - axes = mplot3d.Axes3D(figure) - - # Load the STL files and add the vectors to the plot - stl_mesh = mesh.Mesh.from_file('aux_figure.stl') - os.remove('aux_figure.stl') - axes.add_collection3d( - mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) - - # Get the limits of the axis and center the geometry - max_dim = np.array([ \ - np.max(stl_mesh.vectors[:, :, 0]) / 1000, \ - np.max(stl_mesh.vectors[:, :, 1]) / 1000, \ - np.max(stl_mesh.vectors[:, :, 2]) / 1000]) - min_dim = np.array([ \ - np.min(stl_mesh.vectors[:, :, 0]) / 1000, \ - np.min(stl_mesh.vectors[:, :, 1]) / 1000, \ - np.min(stl_mesh.vectors[:, :, 2]) / 1000]) - - max_lenght = np.max(max_dim - min_dim) - axes.set_xlim( \ - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, \ - .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) - axes.set_ylim( \ - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, \ - .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) - axes.set_zlim( \ - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, \ - .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) - - # Show the plot to the screen - if not save_fig: - pyplot.show() - else: - figure.savefig(plot_file.split('.')[0] + '.png') - - return figure - - def show(self, show_file=None): - """ - Method to show a file. If `show_file` is not given it plots `self.shape`. - - :param string show_file: the filename you want to show. - """ - if show_file is None: - shape = self.shape - else: - shape = self.load_shape_from_file(show_file) - - display, start_display, __, __ = init_display() - display.FitAll() - display.DisplayShape(shape, update=True) - - # Show the plot to the screen - start_display() + point_xyz = gp_XYZ(*control_point_coordinates) + + gp_point = gp_Pnt(point_xyz) + occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, + gp_point) + i += 1 + + # construct the deformed wire for the trimmed surfaces + wire_maker = BRepBuilderAPI_MakeWire() + tol = ShapeFix_ShapeTolerance() + brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + self.tolerance).Face() + brep_face = BRep_Tool.Surface(brep) + + # cycle on the edges + edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) + while edge_explorer.More(): + edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) + # edge in the (u,v) coordinates + edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) + # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones + edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge( \ + edge_uv_coordinates[0], brep_face) + edge_phis_coordinates = edge_phis_coordinates_aux.Edge() + tol.SetTolerance(edge_phis_coordinates, self.tolerance) + wire_maker.Add(edge_phis_coordinates) + edge_explorer.Next() + + # grouping the edges in a wire + wire = wire_maker.Wire() + + # trimming the surfaces + brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + wire).Shape() + compound_builder.Add(compound, brep_surf) + n_faces += 1 + faces_explorer.Next() + self.write_shape_to_file(compound, self.outfile) + + @staticmethod + def parse_face(topo_face): + """ + Method to parse a single Face (a single patch nurbs surface). + It returns a matrix with all the coordinates of control points of the + Face and a second list with all the control points related to the + Edges of the Face. + + :param topo_face: the input Face + + :return: mesh_points_face: it is a `n_points`-by-3 matrix containing the + coordinates of the control points of the Face (a nurbs surface) + + :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix + + :rtype: tuple(numpy.ndarray, list) + + """ + # get some Face - Edge - Vertex data map information + mesh_points_edge = [] + face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) + # loop on wires per face + while face_exp_wire.More(): + twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) + wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) + # loop on edges per wire + while wire_exp_edge.More(): + edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) + bspline_converter = BRepBuilderAPI_NurbsConvert(edge) + bspline_converter.Perform(edge) + bspline_tshape_edge = bspline_converter.Shape() + h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( + bspline_tshape_edge)) + h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) + bspline_geom_edge = h_bspline_edge.GetObject() + + nb_poles = bspline_geom_edge.NbPoles() + + # Edge geometric properties + edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) + bspline_geom_edge.Poles(edge_ctrlpts) + + points_single_edge = np.zeros((0, 3)) + for i in range(1, nb_poles + 1): + ctrlpt = edge_ctrlpts.Value(i) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + points_single_edge = np.append(points_single_edge, + ctrlpt_position, + axis=0) + + mesh_points_edge.append(points_single_edge) + + wire_exp_edge.Next() + + face_exp_wire.Next() + # extract mesh points (control points) on Face + mesh_points_face = np.zeros((0, 3)) + # convert Face to Geom B-spline Face + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + # get access to control points (poles) + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) + bsurface.Poles(ctrlpts) + + for indice_u_direction in range(1, nb_u + 1): + for indice_v_direction in range(1, nb_v + 1): + ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + mesh_points_face = np.append(mesh_points_face, + ctrlpt_position, axis=0) + + return mesh_points_face, mesh_points_edge + + def parse_shape(self, filename): + """ + Method to parse a Shape with multiple objects (1 compound = multi-shells + and 1 shell = multi-faces) + It returns a list of matrix with all the coordinates of control points + of each Face and a second list with all the control points related to + Edges of each Face. + + :param filename: the input filename. + + :return: list of (mesh_points: `n_points`-by-3 matrix containing + the coordinates of the control points of the Face (surface), + edge_points: it is a list of numpy.narray) + :rtype: a list of shells + + """ + self.infile = filename + self.shape = self.load_shape_from_file(filename) + + # parse and get control points + l_shells = [] # an empty list of shells + n_shells = 0 + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + + # cycle on shells + while shells_explorer.More(): + topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) + shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell + + # cycle on faces + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer + .Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() + + l_shells.append(l_faces) + n_shells += 1 + shells_explorer.Next() + + return l_shells + + @staticmethod + def write_edge(points_edge, topo_edge): + """ + Method to recreate an Edge associated to a geometric curve + after the modification of its points. + :param points_edge: the deformed points array. + :param topo_edge: the Edge to be modified + :return: Edge (Shape) + + :rtype: TopoDS_Edge + + """ + # convert Edge to Geom B-spline Curve + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) + nurbs_converter.Perform(topo_edge) + nurbs_curve = nurbs_converter.Shape() + topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) + h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) + h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) + bspline_edge_curve = h_bcurve.GetObject() + + # Edge geometric properties + nb_cpt = bspline_edge_curve.NbPoles() + # check consistency + if points_edge.shape[0] != nb_cpt: + raise ValueError("Input control points do not have not have the " + "same number as the geometric edge!") + + else: + for i in range(1, nb_cpt + 1): + cpt = points_edge[i-1] + bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) + + new_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) + + return new_edge.Edge() + + def write_face(self, points_face, list_points_edge, topo_face, toledge): + """ + Method to recreate a Face associated to a geometric surface + after the modification of Face points. It returns a TopoDS_Face. + + :param points_face: the new face points array. + :param list_points_edge: new edge points + :param topo_face: the face to be modified + :param toledge: tolerance on the surface creation after modification + :return: TopoDS_Face (Shape) + + :rtype: TopoDS_Shape + + """ + + # convert Face to Geom B-spline Surface + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) + h_geomsurface = BRep_Tool.Surface(topo_nurbsface) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + # check consistency + if points_face.shape[0] != nb_u * nb_v: + raise ValueError("Input control points do not have not have the " + "same number as the geometric face!") + + # cycle on the face points + indice_cpt = 0 + for iu in range(1, nb_u + 1): + for iv in range(1, nb_v + 1): + cpt = points_face[indice_cpt] + bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) + indice_cpt += 1 + + # create modified new face + new_bspline_tface = BRepBuilderAPI_MakeFace() + toler = OCC.Precision.precision_Confusion() + new_bspline_tface.Init(bsurface.GetHandle(), False, toler) + + # cycle on the wires + face_wires_explorer = TopExp_Explorer(topo_nurbsface + .Oriented(TopAbs_FORWARD), + TopAbs_WIRE) + ind_edge_total = 0 + + while face_wires_explorer.More(): + # get old wire + twire = OCC.TopoDS.topods_Wire(face_wires_explorer.Current()) + + # cycle on the edges + ind_edge = 0 + wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), + TopAbs_EDGE) + # check edges order on the wire + mode3d = True + tolerance_edges = toledge + + wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) + # an edge list + deformed_edges = [] + # cycle on the edges + while wire_explorer_edge.More(): + tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) + new_bspline_tedge = self.write_edge( + list_points_edge[ind_edge_total], tedge) + + deformed_edges.append(new_bspline_tedge) + analyzer = topexp() + vfirst = analyzer.FirstVertex(new_bspline_tedge) + vlast = analyzer.LastVertex(new_bspline_tedge) + pt1 = BRep_Tool.Pnt(vfirst) + pt2 = BRep_Tool.Pnt(vlast) + + wire_order.Add(pt1.XYZ(), pt2.XYZ()) + + ind_edge += 1 + ind_edge_total += 1 + wire_explorer_edge.Next() + + # grouping the edges in a wire, then in the face + # check edges order and connectivity within the wire + wire_order.Perform() + # new wire to be created + stol = ShapeFix_ShapeTolerance() + new_bspline_twire = BRepBuilderAPI_MakeWire() + for order_i in range(1, wire_order.NbEdges() + 1): + deformed_edge_i = wire_order.Ordered(order_i) + if deformed_edge_i > 0: + # insert the deformed edge to the new wire + new_edge_toadd = deformed_edges[deformed_edge_i - 1] + stol.SetTolerance(new_edge_toadd, toledge) + new_bspline_twire.Add(new_edge_toadd) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(new_edge_toadd, toledge * 10.0) + new_bspline_twire.Add(new_edge_toadd) + else: + deformed_edge_revers = deformed_edges[ + np.abs(deformed_edge_i) - 1] + stol.SetTolerance(deformed_edge_revers, toledge) + new_bspline_twire.Add(deformed_edge_revers) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(deformed_edge_revers, toledge * 10.0) + new_bspline_twire.Add(deformed_edge_revers) + # add new wire to the Face + new_bspline_tface.Add(new_bspline_twire.Wire()) + face_wires_explorer.Next() + + return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) + + @staticmethod + def combine_faces(compshape, sew_tolerance): + """ + Method to combine faces in a shell by adding connectivity and continuity + :param compshape: TopoDS_Shape + :param sew_tolerance: tolerance for sewing + :return: Topo_Shell + """ + + offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) + sew = BRepBuilderAPI_Sewing(sew_tolerance) + + face_explorers = TopExp_Explorer(compshape, TopAbs_FACE) + n_faces = 0 + # cycle on Faces + while face_explorers.More(): + tface = OCC.TopoDS.topods.Face(face_explorers.Current()) + sew.Add(tface) + offsew.Add(tface) + n_faces += 1 + face_explorers.Next() + + offsew.Perform() + offsew.Dump() + sew.Perform() + shell = sew.SewedShape() + sew.Dump() + + shell = OCC.TopoDS.topods.Shell(shell) + shell_fixer = ShapeFix_Shell() + shell_fixer.FixFaceOrientation(shell) + + if shell_fixer.Perform(): + print("{} shells fixed! ".format(shell_fixer.NbShells())) + else: + print "Shells not fixed! " + + new_shell = shell_fixer.Shell() + + if OCC.BRepAlgo.brepalgo_IsValid(new_shell): + print "Shell valid! " + else: + print "Shell failed! " + + return new_shell + + def write_shape(self, l_shells, filename, tol): + """ + Method to recreate a TopoDS_Shape associated to a geometric shape + after the modification of points of each Face. It + returns a TopoDS_Shape (Shape). + + :param l_shells: the list of shells after initial parsing + :param filename: the output filename + :param tol: tolerance on the surface creation after modification + :return: None + + """ + self.outfile = filename + # global compound containing multiple shells + global_compound_builder = BRep_Builder() + global_comp = OCC.TopoDS.TopoDS_Compound() + global_compound_builder.MakeCompound(global_comp) + + # cycle on shells (multiple objects) + shape_shells_explorer = TopExp_Explorer(self.shape + .Oriented(TopAbs_FORWARD), + TopAbs_SHELL) + ishell = 0 + + while shape_shells_explorer.More(): + per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer.Current()) + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer(per_shell.Oriented(TopAbs_FORWARD), + TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[ishell][iface][0], + l_shells[ishell][iface][1], + topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(ishell, itype)) + print "==============================================" + + ishell += 1 + shape_shells_explorer.Next() + + self.write_shape_to_file(global_comp, self.outfile) + + def write_shape_to_file(self, shape, filename): + """ + Abstract method to write the 'shape' to the `filename`. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError(\ + "Subclass must implement abstract method " + \ + self.__class__.__name__ + ".write_shape_to_file") + + def plot(self, plot_file=None, save_fig=False): + """ + Method to plot a file. If `plot_file` is not given it plots `self.shape`. + + :param string plot_file: the filename you want to plot. + :param bool save_fig: a flag to save the figure in png or not. If True the + plot is not shown. + + :return: figure: matlplotlib structure for the figure of the chosen geometry + :rtype: matplotlib.pyplot.figure + """ + if plot_file is None: + shape = self.shape + plot_file = self.infile + else: + shape = self.load_shape_from_file(plot_file) + + stl_writer = StlAPI_Writer() + # Do not switch SetASCIIMode() from False to True. + stl_writer.SetASCIIMode(False) + stl_writer.Write(shape, 'aux_figure.stl') + + # Create a new plot + figure = pyplot.figure() + axes = mplot3d.Axes3D(figure) + + # Load the STL files and add the vectors to the plot + stl_mesh = mesh.Mesh.from_file('aux_figure.stl') + os.remove('aux_figure.stl') + axes.add_collection3d( + mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) + + # Get the limits of the axis and center the geometry + max_dim = np.array([ \ + np.max(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.max(stl_mesh.vectors[:, :, 1]) / 1000, \ + np.max(stl_mesh.vectors[:, :, 2]) / 1000]) + min_dim = np.array([ \ + np.min(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.min(stl_mesh.vectors[:, :, 1]) / 1000, \ + np.min(stl_mesh.vectors[:, :, 2]) / 1000]) + + max_lenght = np.max(max_dim - min_dim) + axes.set_xlim( \ + -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, \ + .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) + axes.set_ylim( \ + -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, \ + .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) + axes.set_zlim( \ + -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, \ + .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) + + # Show the plot to the screen + if not save_fig: + pyplot.show() + else: + figure.savefig(plot_file.split('.')[0] + '.png') + + return figure + + def show(self, show_file=None): + """ + Method to show a file. If `show_file` is not given it plots `self.shape`. + + :param string show_file: the filename you want to show. + """ + if show_file is None: + shape = self.shape + else: + shape = self.load_shape_from_file(show_file) + + display, start_display, __, __ = init_display() + display.FitAll() + display.DisplayShape(shape, update=True) + + # Show the plot to the screen + start_display() From 0b9caa6f8e80e4703825dafdf53696f7cddac85c Mon Sep 17 00:00:00 2001 From: Zhifan Date: Wed, 14 Jun 2017 14:59:10 +0200 Subject: [PATCH 7/9] Add OCC.Precision --- pygem/nurbshandler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index 49d434b..9ca3854 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -17,6 +17,7 @@ from OCC.GeomConvert import geomconvert_CurveToBSplineCurve from OCC.ShapeFix import (ShapeFix_ShapeTolerance, ShapeFix_Shell) from OCC.ShapeAnalysis import ShapeAnalysis_WireOrder +import OCC.Precision from OCC.StlAPI import StlAPI_Writer from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE, TopAbs_FORWARD, TopAbs_SHELL) From 564ccf8894c6220723fbfb6a058820b21ade3de3 Mon Sep 17 00:00:00 2001 From: Zhifan Date: Wed, 14 Jun 2017 16:06:34 +0200 Subject: [PATCH 8/9] Add check_topology() --- pygem/nurbshandler.py | 129 +++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 21 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index 9ca3854..cb1f732 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -316,6 +316,38 @@ def parse_face(topo_face): return mesh_points_face, mesh_points_edge + def check_topology(self): + """ + Method to check the topology of imported geometry. + :return: 0: 1 solid = 1 shell = n faces + 1: 1 solid = 0 shell = n free faces + 2: 1 solid = n shell = n faces (1 shell = 1 face) + """ + # read shells and faces + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + n_shells = 0 + while shells_explorer.More(): + n_shells += 1 + shells_explorer.Next() + + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + while faces_explorer.More(): + n_faces += 1 + faces_explorer.Next() + + print("##############################################\n" + "Model statistics -- Nb Shells: {0} Faces: {1} \n" + "----------------------------------------------\n" + .format(n_shells, n_faces)) + + if n_shells == 0: + self.check_topo = 1 + elif n_shells == n_faces: + self.check_topo = 2 + else: + self.check_topo = 0 + def parse_shape(self, filename): """ Method to parse a Shape with multiple objects (1 compound = multi-shells @@ -335,18 +367,39 @@ def parse_shape(self, filename): self.infile = filename self.shape = self.load_shape_from_file(filename) + self.check_topology() + # parse and get control points l_shells = [] # an empty list of shells n_shells = 0 - shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) - # cycle on shells - while shells_explorer.More(): - topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) - shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) - l_faces = [] # an empty list of faces per shell + if self.check_topo == 0: + + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + + # cycle on shells + while shells_explorer.More(): + topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) + shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell + + # cycle on faces + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer + .Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() + + l_shells.append(l_faces) + n_shells += 1 + shells_explorer.Next() + + else: + # cycle only on faces + shell_faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell - # cycle on faces while shell_faces_explorer.More(): topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer .Current()) @@ -356,7 +409,6 @@ def parse_shape(self, filename): l_shells.append(l_faces) n_shells += 1 - shells_explorer.Next() return l_shells @@ -575,27 +627,63 @@ def write_shape(self, l_shells, filename, tol): global_comp = OCC.TopoDS.TopoDS_Compound() global_compound_builder.MakeCompound(global_comp) - # cycle on shells (multiple objects) - shape_shells_explorer = TopExp_Explorer(self.shape - .Oriented(TopAbs_FORWARD), - TopAbs_SHELL) - ishell = 0 + if self.check_topo == 0: + # cycle on shells (multiple objects) + shape_shells_explorer = TopExp_Explorer(self.shape + .Oriented(TopAbs_FORWARD), + TopAbs_SHELL) + ishell = 0 + + while shape_shells_explorer.More(): + per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer + .Current()) + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer(per_shell + .Oriented(TopAbs_FORWARD), + TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[ishell][iface][0], + l_shells[ishell][iface][1], + topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(ishell, itype)) + print "==============================================" + + ishell += 1 + shape_shells_explorer.Next() - while shape_shells_explorer.More(): - per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer.Current()) + else: + # cycle on faces # a local compound containing a shell compound_builder = BRep_Builder() comp = OCC.TopoDS.TopoDS_Compound() compound_builder.MakeCompound(comp) # cycle on faces - faces_explorer = TopExp_Explorer(per_shell.Oriented(TopAbs_FORWARD), + faces_explorer = TopExp_Explorer(self.shape.Oriented(TopAbs_FORWARD), TopAbs_FACE) iface = 0 while faces_explorer.More(): topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) - newface = self.write_face(l_shells[ishell][iface][0], - l_shells[ishell][iface][1], + newface = self.write_face(l_shells[0][iface][0], + l_shells[0][iface][1], topoface, tol) # add face to compound @@ -608,11 +696,10 @@ def write_shape(self, l_shells, filename, tol): # add the new shell to the global compound global_compound_builder.Add(global_comp, new_shell) - print("Shell {0} of type {1} Processed ".format(ishell, itype)) + print("Shell {0} of type {1} Processed ".format(0, itype)) print "==============================================" - ishell += 1 - shape_shells_explorer.Next() + self.write_shape_to_file(global_comp, self.outfile) From 47af90c8b41dbcf84ecce97570a72ca3767b12fa Mon Sep 17 00:00:00 2001 From: Zhifan Date: Thu, 15 Jun 2017 16:32:56 +0200 Subject: [PATCH 9/9] Convert indentation: spaces to tabs --- pygem/nurbshandler.py | 1528 ++++++++++++++++++++--------------------- 1 file changed, 764 insertions(+), 764 deletions(-) diff --git a/pygem/nurbshandler.py b/pygem/nurbshandler.py index cb1f732..b995236 100644 --- a/pygem/nurbshandler.py +++ b/pygem/nurbshandler.py @@ -8,10 +8,10 @@ import OCC.TopoDS from OCC.BRep import (BRep_Tool, BRep_Builder, BRep_Tool_Curve) from OCC.BRepBuilderAPI import (BRepBuilderAPI_MakeEdge, - BRepBuilderAPI_MakeFace, - BRepBuilderAPI_NurbsConvert, - BRepBuilderAPI_MakeWire, - BRepBuilderAPI_Sewing) + BRepBuilderAPI_MakeFace, + BRepBuilderAPI_NurbsConvert, + BRepBuilderAPI_MakeWire, + BRepBuilderAPI_Sewing) from OCC.Display.SimpleGui import init_display from OCC.GeomConvert import geomconvert_SurfaceToBSplineSurface from OCC.GeomConvert import geomconvert_CurveToBSplineCurve @@ -20,7 +20,7 @@ import OCC.Precision from OCC.StlAPI import StlAPI_Writer from OCC.TopAbs import (TopAbs_FACE, TopAbs_EDGE, TopAbs_WIRE, - TopAbs_FORWARD, TopAbs_SHELL) + TopAbs_FORWARD, TopAbs_SHELL) from OCC.TopExp import (TopExp_Explorer, topexp) from OCC.gp import (gp_Pnt, gp_XYZ) from OCC.TColgp import (TColgp_Array1OfPnt, TColgp_Array2OfPnt) @@ -32,762 +32,762 @@ class NurbsHandler(fh.FileHandler): - """ - Nurbs file handler base class - - :cvar string infile: name of the input file to be processed. - :cvar string outfile: name of the output file where to write in. - :cvar list control_point_position: index of the first NURBS control point (or pole) - of each face of the files. - :cvar TopoDS_Shape shape: shape meant for modification. - :cvar float tolerance: tolerance for the construction of the faces and wires - in the write function. Default value is 1e-6. - - .. warning:: - - - For non trivial geometries it could be necessary to increase the tolerance. - Linking edges into a single wire and then trimming the surface with the wire - can be hard for the software, especially when the starting CAD has not been - made for analysis but for design purposes. - """ - - def __init__(self): - super(NurbsHandler, self).__init__() - self._control_point_position = None - self.tolerance = 1e-6 - self.shape = None - - def _check_infile_instantiation(self): - """ - This private method checks if `self.infile` and `self.shape' are instantiated. If not it means - that nobody called the parse method and at least one of them is None` If the check fails - it raises a RuntimeError. - - """ - if not self.shape or not self.infile: - raise RuntimeError( - "You can not write a file without having parsed one.") - - def load_shape_from_file(self, filename): - """ - Abstract method to load a specific file as a shape. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError("Subclass must implement abstract method " + \ - self.__class__.__name__ + ".load_shape_from_file") - - - def parse(self, filename): - """ - Method to parse the file `filename`. It returns a matrix with all the coordinates. - - :param string filename: name of the input file. - - :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of - the points of the mesh - :rtype: numpy.ndarray - - """ - self.infile = filename - self.shape = self.load_shape_from_file(filename) - - # cycle on the faces to get the control points - # init some quantities - n_faces = 0 - control_point_position = [0] - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - mesh_points = np.zeros(shape=(0, 3)) - - while faces_explorer.More(): - # performing some conversions to get the right format (BSplineSurface) - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - - # openCascade object - occ_face = bspline_face.GetObject() - - # extract the Control Points of each face - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - control_polygon_coordinates = np.zeros( \ - shape=(n_poles_u * n_poles_v, 3)) - - # cycle over the poles to get their coordinates - i = 0 - - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = occ_face.Pole(\ - - pole_u_direction + 1, pole_v_direction + 1) - control_polygon_coordinates[i, :] = [ - control_point_coordinates.X(), \ - control_point_coordinates.Y(), \ - control_point_coordinates.Z()] - i += 1 - # pushing the control points coordinates to the mesh_points array (used for FFD) - mesh_points = np.append(mesh_points, control_polygon_coordinates, - axis=0) - control_point_position.append( - control_point_position[-1] + n_poles_u * n_poles_v) - - n_faces += 1 - faces_explorer.Next() - self._control_point_position = control_point_position - return mesh_points - - def write(self, mesh_points, filename, tolerance=None): - """ - Writes a output file, called filename, copying all the structures from self.filename but - the coordinates. mesh_points is a matrix that contains the new coordinates to - write in the output file. - - :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing - the coordinates of the points of the mesh - :param string filename: name of the output file. - :param float tolerance: tolerance for the construction of the faces and wires - in the write function. If not given it uses `self.tolerance`. - """ - self._check_filename_type(filename) - self._check_extension(filename) - self._check_infile_instantiation() - - self.outfile = filename - - if tolerance is not None: - self.tolerance = tolerance - - # cycle on the faces to update the control points position - # init some quantities - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - n_faces = 0 - control_point_position = self._control_point_position - - compound_builder = BRep_Builder() - compound = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(compound) - - while faces_explorer.More(): - # similar to the parser method - face = OCC.TopoDS.topods_Face(faces_explorer.Current()) - nurbs_converter = BRepBuilderAPI_NurbsConvert(face) - nurbs_converter.Perform(face) - nurbs_face = nurbs_converter.Shape() - face_aux = OCC.TopoDS.topods_Face(nurbs_face) - brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) - bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) - occ_face = bspline_face.GetObject() - - n_poles_u = occ_face.NbUPoles() - n_poles_v = occ_face.NbVPoles() - - i = 0 - - for pole_u_direction in range(n_poles_u): - for pole_v_direction in range(n_poles_v): - control_point_coordinates = ( - mesh_points[i + control_point_position[n_faces], :] - ) - point_xyz = gp_XYZ(*control_point_coordinates) - - gp_point = gp_Pnt(point_xyz) - occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, - gp_point) - i += 1 - - # construct the deformed wire for the trimmed surfaces - wire_maker = BRepBuilderAPI_MakeWire() - tol = ShapeFix_ShapeTolerance() - brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), - self.tolerance).Face() - brep_face = BRep_Tool.Surface(brep) - - # cycle on the edges - edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) - while edge_explorer.More(): - edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) - # edge in the (u,v) coordinates - edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) - # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones - edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge( \ - edge_uv_coordinates[0], brep_face) - edge_phis_coordinates = edge_phis_coordinates_aux.Edge() - tol.SetTolerance(edge_phis_coordinates, self.tolerance) - wire_maker.Add(edge_phis_coordinates) - edge_explorer.Next() - - # grouping the edges in a wire - wire = wire_maker.Wire() - - # trimming the surfaces - brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), - wire).Shape() - compound_builder.Add(compound, brep_surf) - n_faces += 1 - faces_explorer.Next() - self.write_shape_to_file(compound, self.outfile) - - @staticmethod - def parse_face(topo_face): - """ - Method to parse a single Face (a single patch nurbs surface). - It returns a matrix with all the coordinates of control points of the - Face and a second list with all the control points related to the - Edges of the Face. - - :param topo_face: the input Face - - :return: mesh_points_face: it is a `n_points`-by-3 matrix containing the - coordinates of the control points of the Face (a nurbs surface) - - :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix - - :rtype: tuple(numpy.ndarray, list) - - """ - # get some Face - Edge - Vertex data map information - mesh_points_edge = [] - face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) - # loop on wires per face - while face_exp_wire.More(): - twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) - wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) - # loop on edges per wire - while wire_exp_edge.More(): - edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) - bspline_converter = BRepBuilderAPI_NurbsConvert(edge) - bspline_converter.Perform(edge) - bspline_tshape_edge = bspline_converter.Shape() - h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( - bspline_tshape_edge)) - h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) - bspline_geom_edge = h_bspline_edge.GetObject() - - nb_poles = bspline_geom_edge.NbPoles() - - # Edge geometric properties - edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) - bspline_geom_edge.Poles(edge_ctrlpts) - - points_single_edge = np.zeros((0, 3)) - for i in range(1, nb_poles + 1): - ctrlpt = edge_ctrlpts.Value(i) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - points_single_edge = np.append(points_single_edge, - ctrlpt_position, - axis=0) - - mesh_points_edge.append(points_single_edge) - - wire_exp_edge.Next() - - face_exp_wire.Next() - # extract mesh points (control points) on Face - mesh_points_face = np.zeros((0, 3)) - # convert Face to Geom B-spline Face - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - # get access to control points (poles) - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) - bsurface.Poles(ctrlpts) - - for indice_u_direction in range(1, nb_u + 1): - for indice_v_direction in range(1, nb_v + 1): - ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) - ctrlpt_position = np.array([[ctrlpt.Coord(1), - ctrlpt.Coord(2), - ctrlpt.Coord(3)]]) - mesh_points_face = np.append(mesh_points_face, - ctrlpt_position, axis=0) - - return mesh_points_face, mesh_points_edge - - def check_topology(self): - """ - Method to check the topology of imported geometry. - :return: 0: 1 solid = 1 shell = n faces - 1: 1 solid = 0 shell = n free faces - 2: 1 solid = n shell = n faces (1 shell = 1 face) - """ - # read shells and faces - shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) - n_shells = 0 - while shells_explorer.More(): - n_shells += 1 - shells_explorer.Next() - - faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - n_faces = 0 - while faces_explorer.More(): - n_faces += 1 - faces_explorer.Next() - - print("##############################################\n" - "Model statistics -- Nb Shells: {0} Faces: {1} \n" - "----------------------------------------------\n" - .format(n_shells, n_faces)) - - if n_shells == 0: - self.check_topo = 1 - elif n_shells == n_faces: - self.check_topo = 2 - else: - self.check_topo = 0 - - def parse_shape(self, filename): - """ - Method to parse a Shape with multiple objects (1 compound = multi-shells - and 1 shell = multi-faces) - It returns a list of matrix with all the coordinates of control points - of each Face and a second list with all the control points related to - Edges of each Face. - - :param filename: the input filename. - - :return: list of (mesh_points: `n_points`-by-3 matrix containing - the coordinates of the control points of the Face (surface), - edge_points: it is a list of numpy.narray) - :rtype: a list of shells - - """ - self.infile = filename - self.shape = self.load_shape_from_file(filename) - - self.check_topology() - - # parse and get control points - l_shells = [] # an empty list of shells - n_shells = 0 - - if self.check_topo == 0: - - shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) - - # cycle on shells - while shells_explorer.More(): - topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) - shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) - l_faces = [] # an empty list of faces per shell - - # cycle on faces - while shell_faces_explorer.More(): - topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer - .Current()) - mesh_point, edge_point = self.parse_face(topo_face) - l_faces.append((mesh_point, edge_point)) - shell_faces_explorer.Next() - - l_shells.append(l_faces) - n_shells += 1 - shells_explorer.Next() - - else: - # cycle only on faces - shell_faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) - l_faces = [] # an empty list of faces per shell - - while shell_faces_explorer.More(): - topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer - .Current()) - mesh_point, edge_point = self.parse_face(topo_face) - l_faces.append((mesh_point, edge_point)) - shell_faces_explorer.Next() - - l_shells.append(l_faces) - n_shells += 1 - - return l_shells - - @staticmethod - def write_edge(points_edge, topo_edge): - """ - Method to recreate an Edge associated to a geometric curve - after the modification of its points. - :param points_edge: the deformed points array. - :param topo_edge: the Edge to be modified - :return: Edge (Shape) - - :rtype: TopoDS_Edge - - """ - # convert Edge to Geom B-spline Curve - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) - nurbs_converter.Perform(topo_edge) - nurbs_curve = nurbs_converter.Shape() - topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) - h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) - h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) - bspline_edge_curve = h_bcurve.GetObject() - - # Edge geometric properties - nb_cpt = bspline_edge_curve.NbPoles() - # check consistency - if points_edge.shape[0] != nb_cpt: - raise ValueError("Input control points do not have not have the " - "same number as the geometric edge!") - - else: - for i in range(1, nb_cpt + 1): - cpt = points_edge[i-1] - bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) - - new_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) - - return new_edge.Edge() - - def write_face(self, points_face, list_points_edge, topo_face, toledge): - """ - Method to recreate a Face associated to a geometric surface - after the modification of Face points. It returns a TopoDS_Face. - - :param points_face: the new face points array. - :param list_points_edge: new edge points - :param topo_face: the face to be modified - :param toledge: tolerance on the surface creation after modification - :return: TopoDS_Face (Shape) - - :rtype: TopoDS_Shape - - """ - - # convert Face to Geom B-spline Surface - nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) - nurbs_converter.Perform(topo_face) - nurbs_face = nurbs_converter.Shape() - topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) - h_geomsurface = BRep_Tool.Surface(topo_nurbsface) - h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) - bsurface = h_bsurface.GetObject() - - nb_u = bsurface.NbUPoles() - nb_v = bsurface.NbVPoles() - # check consistency - if points_face.shape[0] != nb_u * nb_v: - raise ValueError("Input control points do not have not have the " - "same number as the geometric face!") - - # cycle on the face points - indice_cpt = 0 - for iu in range(1, nb_u + 1): - for iv in range(1, nb_v + 1): - cpt = points_face[indice_cpt] - bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) - indice_cpt += 1 - - # create modified new face - new_bspline_tface = BRepBuilderAPI_MakeFace() - toler = OCC.Precision.precision_Confusion() - new_bspline_tface.Init(bsurface.GetHandle(), False, toler) - - # cycle on the wires - face_wires_explorer = TopExp_Explorer(topo_nurbsface - .Oriented(TopAbs_FORWARD), - TopAbs_WIRE) - ind_edge_total = 0 - - while face_wires_explorer.More(): - # get old wire - twire = OCC.TopoDS.topods_Wire(face_wires_explorer.Current()) - - # cycle on the edges - ind_edge = 0 - wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), - TopAbs_EDGE) - # check edges order on the wire - mode3d = True - tolerance_edges = toledge - - wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) - # an edge list - deformed_edges = [] - # cycle on the edges - while wire_explorer_edge.More(): - tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) - new_bspline_tedge = self.write_edge( - list_points_edge[ind_edge_total], tedge) - - deformed_edges.append(new_bspline_tedge) - analyzer = topexp() - vfirst = analyzer.FirstVertex(new_bspline_tedge) - vlast = analyzer.LastVertex(new_bspline_tedge) - pt1 = BRep_Tool.Pnt(vfirst) - pt2 = BRep_Tool.Pnt(vlast) - - wire_order.Add(pt1.XYZ(), pt2.XYZ()) - - ind_edge += 1 - ind_edge_total += 1 - wire_explorer_edge.Next() - - # grouping the edges in a wire, then in the face - # check edges order and connectivity within the wire - wire_order.Perform() - # new wire to be created - stol = ShapeFix_ShapeTolerance() - new_bspline_twire = BRepBuilderAPI_MakeWire() - for order_i in range(1, wire_order.NbEdges() + 1): - deformed_edge_i = wire_order.Ordered(order_i) - if deformed_edge_i > 0: - # insert the deformed edge to the new wire - new_edge_toadd = deformed_edges[deformed_edge_i - 1] - stol.SetTolerance(new_edge_toadd, toledge) - new_bspline_twire.Add(new_edge_toadd) - if new_bspline_twire.Error() != 0: - stol.SetTolerance(new_edge_toadd, toledge * 10.0) - new_bspline_twire.Add(new_edge_toadd) - else: - deformed_edge_revers = deformed_edges[ - np.abs(deformed_edge_i) - 1] - stol.SetTolerance(deformed_edge_revers, toledge) - new_bspline_twire.Add(deformed_edge_revers) - if new_bspline_twire.Error() != 0: - stol.SetTolerance(deformed_edge_revers, toledge * 10.0) - new_bspline_twire.Add(deformed_edge_revers) - # add new wire to the Face - new_bspline_tface.Add(new_bspline_twire.Wire()) - face_wires_explorer.Next() - - return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) - - @staticmethod - def combine_faces(compshape, sew_tolerance): - """ - Method to combine faces in a shell by adding connectivity and continuity - :param compshape: TopoDS_Shape - :param sew_tolerance: tolerance for sewing - :return: Topo_Shell - """ - - offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) - sew = BRepBuilderAPI_Sewing(sew_tolerance) - - face_explorers = TopExp_Explorer(compshape, TopAbs_FACE) - n_faces = 0 - # cycle on Faces - while face_explorers.More(): - tface = OCC.TopoDS.topods.Face(face_explorers.Current()) - sew.Add(tface) - offsew.Add(tface) - n_faces += 1 - face_explorers.Next() - - offsew.Perform() - offsew.Dump() - sew.Perform() - shell = sew.SewedShape() - sew.Dump() - - shell = OCC.TopoDS.topods.Shell(shell) - shell_fixer = ShapeFix_Shell() - shell_fixer.FixFaceOrientation(shell) - - if shell_fixer.Perform(): - print("{} shells fixed! ".format(shell_fixer.NbShells())) - else: - print "Shells not fixed! " - - new_shell = shell_fixer.Shell() - - if OCC.BRepAlgo.brepalgo_IsValid(new_shell): - print "Shell valid! " - else: - print "Shell failed! " - - return new_shell - - def write_shape(self, l_shells, filename, tol): - """ - Method to recreate a TopoDS_Shape associated to a geometric shape - after the modification of points of each Face. It - returns a TopoDS_Shape (Shape). - - :param l_shells: the list of shells after initial parsing - :param filename: the output filename - :param tol: tolerance on the surface creation after modification - :return: None - - """ - self.outfile = filename - # global compound containing multiple shells - global_compound_builder = BRep_Builder() - global_comp = OCC.TopoDS.TopoDS_Compound() - global_compound_builder.MakeCompound(global_comp) - - if self.check_topo == 0: - # cycle on shells (multiple objects) - shape_shells_explorer = TopExp_Explorer(self.shape - .Oriented(TopAbs_FORWARD), - TopAbs_SHELL) - ishell = 0 - - while shape_shells_explorer.More(): - per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer - .Current()) - # a local compound containing a shell - compound_builder = BRep_Builder() - comp = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(comp) - - # cycle on faces - faces_explorer = TopExp_Explorer(per_shell - .Oriented(TopAbs_FORWARD), - TopAbs_FACE) - iface = 0 - while faces_explorer.More(): - topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) - newface = self.write_face(l_shells[ishell][iface][0], - l_shells[ishell][iface][1], - topoface, tol) - - # add face to compound - compound_builder.Add(comp, newface) - iface += 1 - faces_explorer.Next() - - new_shell = self.combine_faces(comp, 0.01) - itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) - # add the new shell to the global compound - global_compound_builder.Add(global_comp, new_shell) - - print("Shell {0} of type {1} Processed ".format(ishell, itype)) - print "==============================================" - - ishell += 1 - shape_shells_explorer.Next() - - else: - # cycle on faces - # a local compound containing a shell - compound_builder = BRep_Builder() - comp = OCC.TopoDS.TopoDS_Compound() - compound_builder.MakeCompound(comp) - - # cycle on faces - faces_explorer = TopExp_Explorer(self.shape.Oriented(TopAbs_FORWARD), - TopAbs_FACE) - iface = 0 - while faces_explorer.More(): - topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) - newface = self.write_face(l_shells[0][iface][0], - l_shells[0][iface][1], - topoface, tol) - - # add face to compound - compound_builder.Add(comp, newface) - iface += 1 - faces_explorer.Next() - - new_shell = self.combine_faces(comp, 0.01) - itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) - # add the new shell to the global compound - global_compound_builder.Add(global_comp, new_shell) - - print("Shell {0} of type {1} Processed ".format(0, itype)) - print "==============================================" - - - - self.write_shape_to_file(global_comp, self.outfile) - - def write_shape_to_file(self, shape, filename): - """ - Abstract method to write the 'shape' to the `filename`. - - Not implemented, it has to be implemented in subclasses. - """ - raise NotImplementedError(\ - "Subclass must implement abstract method " + \ - self.__class__.__name__ + ".write_shape_to_file") - - def plot(self, plot_file=None, save_fig=False): - """ - Method to plot a file. If `plot_file` is not given it plots `self.shape`. - - :param string plot_file: the filename you want to plot. - :param bool save_fig: a flag to save the figure in png or not. If True the - plot is not shown. - - :return: figure: matlplotlib structure for the figure of the chosen geometry - :rtype: matplotlib.pyplot.figure - """ - if plot_file is None: - shape = self.shape - plot_file = self.infile - else: - shape = self.load_shape_from_file(plot_file) - - stl_writer = StlAPI_Writer() - # Do not switch SetASCIIMode() from False to True. - stl_writer.SetASCIIMode(False) - stl_writer.Write(shape, 'aux_figure.stl') - - # Create a new plot - figure = pyplot.figure() - axes = mplot3d.Axes3D(figure) - - # Load the STL files and add the vectors to the plot - stl_mesh = mesh.Mesh.from_file('aux_figure.stl') - os.remove('aux_figure.stl') - axes.add_collection3d( - mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) - - # Get the limits of the axis and center the geometry - max_dim = np.array([ \ - np.max(stl_mesh.vectors[:, :, 0]) / 1000, \ - np.max(stl_mesh.vectors[:, :, 1]) / 1000, \ - np.max(stl_mesh.vectors[:, :, 2]) / 1000]) - min_dim = np.array([ \ - np.min(stl_mesh.vectors[:, :, 0]) / 1000, \ - np.min(stl_mesh.vectors[:, :, 1]) / 1000, \ - np.min(stl_mesh.vectors[:, :, 2]) / 1000]) - - max_lenght = np.max(max_dim - min_dim) - axes.set_xlim( \ - -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, \ - .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) - axes.set_ylim( \ - -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, \ - .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) - axes.set_zlim( \ - -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, \ - .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) - - # Show the plot to the screen - if not save_fig: - pyplot.show() - else: - figure.savefig(plot_file.split('.')[0] + '.png') - - return figure - - def show(self, show_file=None): - """ - Method to show a file. If `show_file` is not given it plots `self.shape`. - - :param string show_file: the filename you want to show. - """ - if show_file is None: - shape = self.shape - else: - shape = self.load_shape_from_file(show_file) - - display, start_display, __, __ = init_display() - display.FitAll() - display.DisplayShape(shape, update=True) - - # Show the plot to the screen - start_display() + """ + Nurbs file handler base class + + :cvar string infile: name of the input file to be processed. + :cvar string outfile: name of the output file where to write in. + :cvar list control_point_position: index of the first NURBS control point (or pole) + of each face of the files. + :cvar TopoDS_Shape shape: shape meant for modification. + :cvar float tolerance: tolerance for the construction of the faces and wires + in the write function. Default value is 1e-6. + + .. warning:: + + - For non trivial geometries it could be necessary to increase the tolerance. + Linking edges into a single wire and then trimming the surface with the wire + can be hard for the software, especially when the starting CAD has not been + made for analysis but for design purposes. + """ + + def __init__(self): + super(NurbsHandler, self).__init__() + self._control_point_position = None + self.tolerance = 1e-6 + self.shape = None + + def _check_infile_instantiation(self): + """ + This private method checks if `self.infile` and `self.shape' are instantiated. If not it means + that nobody called the parse method and at least one of them is None` If the check fails + it raises a RuntimeError. + + """ + if not self.shape or not self.infile: + raise RuntimeError( + "You can not write a file without having parsed one.") + + def load_shape_from_file(self, filename): + """ + Abstract method to load a specific file as a shape. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError("Subclass must implement abstract method " + \ + self.__class__.__name__ + ".load_shape_from_file") + + + def parse(self, filename): + """ + Method to parse the file `filename`. It returns a matrix with all the coordinates. + + :param string filename: name of the input file. + + :return: mesh_points: it is a `n_points`-by-3 matrix containing the coordinates of + the points of the mesh + :rtype: numpy.ndarray + + """ + self.infile = filename + self.shape = self.load_shape_from_file(filename) + + # cycle on the faces to get the control points + # init some quantities + n_faces = 0 + control_point_position = [0] + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + mesh_points = np.zeros(shape=(0, 3)) + + while faces_explorer.More(): + # performing some conversions to get the right format (BSplineSurface) + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + + # openCascade object + occ_face = bspline_face.GetObject() + + # extract the Control Points of each face + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + control_polygon_coordinates = np.zeros( \ + shape=(n_poles_u * n_poles_v, 3)) + + # cycle over the poles to get their coordinates + i = 0 + + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = occ_face.Pole(\ + + pole_u_direction + 1, pole_v_direction + 1) + control_polygon_coordinates[i, :] = [ + control_point_coordinates.X(), \ + control_point_coordinates.Y(), \ + control_point_coordinates.Z()] + i += 1 + # pushing the control points coordinates to the mesh_points array (used for FFD) + mesh_points = np.append(mesh_points, control_polygon_coordinates, + axis=0) + control_point_position.append( + control_point_position[-1] + n_poles_u * n_poles_v) + + n_faces += 1 + faces_explorer.Next() + self._control_point_position = control_point_position + return mesh_points + + def write(self, mesh_points, filename, tolerance=None): + """ + Writes a output file, called filename, copying all the structures from self.filename but + the coordinates. mesh_points is a matrix that contains the new coordinates to + write in the output file. + + :param numpy.ndarray mesh_points: it is a `n_points`-by-3 matrix containing + the coordinates of the points of the mesh + :param string filename: name of the output file. + :param float tolerance: tolerance for the construction of the faces and wires + in the write function. If not given it uses `self.tolerance`. + """ + self._check_filename_type(filename) + self._check_extension(filename) + self._check_infile_instantiation() + + self.outfile = filename + + if tolerance is not None: + self.tolerance = tolerance + + # cycle on the faces to update the control points position + # init some quantities + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + control_point_position = self._control_point_position + + compound_builder = BRep_Builder() + compound = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(compound) + + while faces_explorer.More(): + # similar to the parser method + face = OCC.TopoDS.topods_Face(faces_explorer.Current()) + nurbs_converter = BRepBuilderAPI_NurbsConvert(face) + nurbs_converter.Perform(face) + nurbs_face = nurbs_converter.Shape() + face_aux = OCC.TopoDS.topods_Face(nurbs_face) + brep_face = BRep_Tool.Surface(OCC.TopoDS.topods_Face(nurbs_face)) + bspline_face = geomconvert_SurfaceToBSplineSurface(brep_face) + occ_face = bspline_face.GetObject() + + n_poles_u = occ_face.NbUPoles() + n_poles_v = occ_face.NbVPoles() + + i = 0 + + for pole_u_direction in range(n_poles_u): + for pole_v_direction in range(n_poles_v): + control_point_coordinates = ( + mesh_points[i + control_point_position[n_faces], :] + ) + point_xyz = gp_XYZ(*control_point_coordinates) + + gp_point = gp_Pnt(point_xyz) + occ_face.SetPole(pole_u_direction + 1, pole_v_direction + 1, + gp_point) + i += 1 + + # construct the deformed wire for the trimmed surfaces + wire_maker = BRepBuilderAPI_MakeWire() + tol = ShapeFix_ShapeTolerance() + brep = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + self.tolerance).Face() + brep_face = BRep_Tool.Surface(brep) + + # cycle on the edges + edge_explorer = TopExp_Explorer(nurbs_face, TopAbs_EDGE) + while edge_explorer.More(): + edge = OCC.TopoDS.topods_Edge(edge_explorer.Current()) + # edge in the (u,v) coordinates + edge_uv_coordinates = BRep_Tool.CurveOnSurface(edge, face_aux) + # evaluating the new edge: same (u,v) coordinates, but different (x,y,x) ones + edge_phis_coordinates_aux = BRepBuilderAPI_MakeEdge( \ + edge_uv_coordinates[0], brep_face) + edge_phis_coordinates = edge_phis_coordinates_aux.Edge() + tol.SetTolerance(edge_phis_coordinates, self.tolerance) + wire_maker.Add(edge_phis_coordinates) + edge_explorer.Next() + + # grouping the edges in a wire + wire = wire_maker.Wire() + + # trimming the surfaces + brep_surf = BRepBuilderAPI_MakeFace(occ_face.GetHandle(), + wire).Shape() + compound_builder.Add(compound, brep_surf) + n_faces += 1 + faces_explorer.Next() + self.write_shape_to_file(compound, self.outfile) + + @staticmethod + def parse_face(topo_face): + """ + Method to parse a single Face (a single patch nurbs surface). + It returns a matrix with all the coordinates of control points of the + Face and a second list with all the control points related to the + Edges of the Face. + + :param topo_face: the input Face + + :return: mesh_points_face: it is a `n_points`-by-3 matrix containing the + coordinates of the control points of the Face (a nurbs surface) + + :return: mesh_points_edge: it is a list of `n_points`-by-3 matrix + + :rtype: tuple(numpy.ndarray, list) + + """ + # get some Face - Edge - Vertex data map information + mesh_points_edge = [] + face_exp_wire = TopExp_Explorer(topo_face, TopAbs_WIRE) + # loop on wires per face + while face_exp_wire.More(): + twire = OCC.TopoDS.topods_Wire(face_exp_wire.Current()) + wire_exp_edge = TopExp_Explorer(twire, TopAbs_EDGE) + # loop on edges per wire + while wire_exp_edge.More(): + edge = OCC.TopoDS.topods_Edge(wire_exp_edge.Current()) + bspline_converter = BRepBuilderAPI_NurbsConvert(edge) + bspline_converter.Perform(edge) + bspline_tshape_edge = bspline_converter.Shape() + h_geom_edge, a, b = BRep_Tool_Curve(OCC.TopoDS.topods_Edge( + bspline_tshape_edge)) + h_bspline_edge = geomconvert_CurveToBSplineCurve(h_geom_edge) + bspline_geom_edge = h_bspline_edge.GetObject() + + nb_poles = bspline_geom_edge.NbPoles() + + # Edge geometric properties + edge_ctrlpts = TColgp_Array1OfPnt(1, nb_poles) + bspline_geom_edge.Poles(edge_ctrlpts) + + points_single_edge = np.zeros((0, 3)) + for i in range(1, nb_poles + 1): + ctrlpt = edge_ctrlpts.Value(i) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + points_single_edge = np.append(points_single_edge, + ctrlpt_position, + axis=0) + + mesh_points_edge.append(points_single_edge) + + wire_exp_edge.Next() + + face_exp_wire.Next() + # extract mesh points (control points) on Face + mesh_points_face = np.zeros((0, 3)) + # convert Face to Geom B-spline Face + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + h_geomsurface = BRep_Tool.Surface(OCC.TopoDS.topods.Face(nurbs_face)) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + # get access to control points (poles) + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + ctrlpts = TColgp_Array2OfPnt(1, nb_u, 1, nb_v) + bsurface.Poles(ctrlpts) + + for indice_u_direction in range(1, nb_u + 1): + for indice_v_direction in range(1, nb_v + 1): + ctrlpt = ctrlpts.Value(indice_u_direction, indice_v_direction) + ctrlpt_position = np.array([[ctrlpt.Coord(1), + ctrlpt.Coord(2), + ctrlpt.Coord(3)]]) + mesh_points_face = np.append(mesh_points_face, + ctrlpt_position, axis=0) + + return mesh_points_face, mesh_points_edge + + def check_topology(self): + """ + Method to check the topology of imported geometry. + :return: 0: 1 solid = 1 shell = n faces + 1: 1 solid = 0 shell = n free faces + 2: 1 solid = n shell = n faces (1 shell = 1 face) + """ + # read shells and faces + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + n_shells = 0 + while shells_explorer.More(): + n_shells += 1 + shells_explorer.Next() + + faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + n_faces = 0 + while faces_explorer.More(): + n_faces += 1 + faces_explorer.Next() + + print("##############################################\n" + "Model statistics -- Nb Shells: {0} Faces: {1} \n" + "----------------------------------------------\n" + .format(n_shells, n_faces)) + + if n_shells == 0: + self.check_topo = 1 + elif n_shells == n_faces: + self.check_topo = 2 + else: + self.check_topo = 0 + + def parse_shape(self, filename): + """ + Method to parse a Shape with multiple objects (1 compound = multi-shells + and 1 shell = multi-faces) + It returns a list of matrix with all the coordinates of control points + of each Face and a second list with all the control points related to + Edges of each Face. + + :param filename: the input filename. + + :return: list of (mesh_points: `n_points`-by-3 matrix containing + the coordinates of the control points of the Face (surface), + edge_points: it is a list of numpy.narray) + :rtype: a list of shells + + """ + self.infile = filename + self.shape = self.load_shape_from_file(filename) + + self.check_topology() + + # parse and get control points + l_shells = [] # an empty list of shells + n_shells = 0 + + if self.check_topo == 0: + + shells_explorer = TopExp_Explorer(self.shape, TopAbs_SHELL) + + # cycle on shells + while shells_explorer.More(): + topo_shell = OCC.TopoDS.topods.Shell(shells_explorer.Current()) + shell_faces_explorer = TopExp_Explorer(topo_shell, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell + + # cycle on faces + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer + .Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() + + l_shells.append(l_faces) + n_shells += 1 + shells_explorer.Next() + + else: + # cycle only on faces + shell_faces_explorer = TopExp_Explorer(self.shape, TopAbs_FACE) + l_faces = [] # an empty list of faces per shell + + while shell_faces_explorer.More(): + topo_face = OCC.TopoDS.topods.Face(shell_faces_explorer + .Current()) + mesh_point, edge_point = self.parse_face(topo_face) + l_faces.append((mesh_point, edge_point)) + shell_faces_explorer.Next() + + l_shells.append(l_faces) + n_shells += 1 + + return l_shells + + @staticmethod + def write_edge(points_edge, topo_edge): + """ + Method to recreate an Edge associated to a geometric curve + after the modification of its points. + :param points_edge: the deformed points array. + :param topo_edge: the Edge to be modified + :return: Edge (Shape) + + :rtype: TopoDS_Edge + + """ + # convert Edge to Geom B-spline Curve + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_edge) + nurbs_converter.Perform(topo_edge) + nurbs_curve = nurbs_converter.Shape() + topo_curve = OCC.TopoDS.topods_Edge(nurbs_curve) + h_geomcurve, param_min, param_max = BRep_Tool.Curve(topo_curve) + h_bcurve = geomconvert_CurveToBSplineCurve(h_geomcurve) + bspline_edge_curve = h_bcurve.GetObject() + + # Edge geometric properties + nb_cpt = bspline_edge_curve.NbPoles() + # check consistency + if points_edge.shape[0] != nb_cpt: + raise ValueError("Input control points do not have not have the " + "same number as the geometric edge!") + + else: + for i in range(1, nb_cpt + 1): + cpt = points_edge[i-1] + bspline_edge_curve.SetPole(i, gp_Pnt(cpt[0], cpt[1], cpt[2])) + + new_edge = BRepBuilderAPI_MakeEdge(bspline_edge_curve.GetHandle()) + + return new_edge.Edge() + + def write_face(self, points_face, list_points_edge, topo_face, toledge): + """ + Method to recreate a Face associated to a geometric surface + after the modification of Face points. It returns a TopoDS_Face. + + :param points_face: the new face points array. + :param list_points_edge: new edge points + :param topo_face: the face to be modified + :param toledge: tolerance on the surface creation after modification + :return: TopoDS_Face (Shape) + + :rtype: TopoDS_Shape + + """ + + # convert Face to Geom B-spline Surface + nurbs_converter = BRepBuilderAPI_NurbsConvert(topo_face) + nurbs_converter.Perform(topo_face) + nurbs_face = nurbs_converter.Shape() + topo_nurbsface = OCC.TopoDS.topods.Face(nurbs_face) + h_geomsurface = BRep_Tool.Surface(topo_nurbsface) + h_bsurface = geomconvert_SurfaceToBSplineSurface(h_geomsurface) + bsurface = h_bsurface.GetObject() + + nb_u = bsurface.NbUPoles() + nb_v = bsurface.NbVPoles() + # check consistency + if points_face.shape[0] != nb_u * nb_v: + raise ValueError("Input control points do not have not have the " + "same number as the geometric face!") + + # cycle on the face points + indice_cpt = 0 + for iu in range(1, nb_u + 1): + for iv in range(1, nb_v + 1): + cpt = points_face[indice_cpt] + bsurface.SetPole(iu, iv, gp_Pnt(cpt[0], cpt[1], cpt[2])) + indice_cpt += 1 + + # create modified new face + new_bspline_tface = BRepBuilderAPI_MakeFace() + toler = OCC.Precision.precision_Confusion() + new_bspline_tface.Init(bsurface.GetHandle(), False, toler) + + # cycle on the wires + face_wires_explorer = TopExp_Explorer(topo_nurbsface + .Oriented(TopAbs_FORWARD), + TopAbs_WIRE) + ind_edge_total = 0 + + while face_wires_explorer.More(): + # get old wire + twire = OCC.TopoDS.topods_Wire(face_wires_explorer.Current()) + + # cycle on the edges + ind_edge = 0 + wire_explorer_edge = TopExp_Explorer(twire.Oriented(TopAbs_FORWARD), + TopAbs_EDGE) + # check edges order on the wire + mode3d = True + tolerance_edges = toledge + + wire_order = ShapeAnalysis_WireOrder(mode3d, tolerance_edges) + # an edge list + deformed_edges = [] + # cycle on the edges + while wire_explorer_edge.More(): + tedge = OCC.TopoDS.topods_Edge(wire_explorer_edge.Current()) + new_bspline_tedge = self.write_edge( + list_points_edge[ind_edge_total], tedge) + + deformed_edges.append(new_bspline_tedge) + analyzer = topexp() + vfirst = analyzer.FirstVertex(new_bspline_tedge) + vlast = analyzer.LastVertex(new_bspline_tedge) + pt1 = BRep_Tool.Pnt(vfirst) + pt2 = BRep_Tool.Pnt(vlast) + + wire_order.Add(pt1.XYZ(), pt2.XYZ()) + + ind_edge += 1 + ind_edge_total += 1 + wire_explorer_edge.Next() + + # grouping the edges in a wire, then in the face + # check edges order and connectivity within the wire + wire_order.Perform() + # new wire to be created + stol = ShapeFix_ShapeTolerance() + new_bspline_twire = BRepBuilderAPI_MakeWire() + for order_i in range(1, wire_order.NbEdges() + 1): + deformed_edge_i = wire_order.Ordered(order_i) + if deformed_edge_i > 0: + # insert the deformed edge to the new wire + new_edge_toadd = deformed_edges[deformed_edge_i - 1] + stol.SetTolerance(new_edge_toadd, toledge) + new_bspline_twire.Add(new_edge_toadd) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(new_edge_toadd, toledge * 10.0) + new_bspline_twire.Add(new_edge_toadd) + else: + deformed_edge_revers = deformed_edges[ + np.abs(deformed_edge_i) - 1] + stol.SetTolerance(deformed_edge_revers, toledge) + new_bspline_twire.Add(deformed_edge_revers) + if new_bspline_twire.Error() != 0: + stol.SetTolerance(deformed_edge_revers, toledge * 10.0) + new_bspline_twire.Add(deformed_edge_revers) + # add new wire to the Face + new_bspline_tface.Add(new_bspline_twire.Wire()) + face_wires_explorer.Next() + + return OCC.TopoDS.topods.Face(new_bspline_tface.Face()) + + @staticmethod + def combine_faces(compshape, sew_tolerance): + """ + Method to combine faces in a shell by adding connectivity and continuity + :param compshape: TopoDS_Shape + :param sew_tolerance: tolerance for sewing + :return: Topo_Shell + """ + + offsew = BRepOffsetAPI_FindContigousEdges(sew_tolerance) + sew = BRepBuilderAPI_Sewing(sew_tolerance) + + face_explorers = TopExp_Explorer(compshape, TopAbs_FACE) + n_faces = 0 + # cycle on Faces + while face_explorers.More(): + tface = OCC.TopoDS.topods.Face(face_explorers.Current()) + sew.Add(tface) + offsew.Add(tface) + n_faces += 1 + face_explorers.Next() + + offsew.Perform() + offsew.Dump() + sew.Perform() + shell = sew.SewedShape() + sew.Dump() + + shell = OCC.TopoDS.topods.Shell(shell) + shell_fixer = ShapeFix_Shell() + shell_fixer.FixFaceOrientation(shell) + + if shell_fixer.Perform(): + print("{} shells fixed! ".format(shell_fixer.NbShells())) + else: + print "Shells not fixed! " + + new_shell = shell_fixer.Shell() + + if OCC.BRepAlgo.brepalgo_IsValid(new_shell): + print "Shell valid! " + else: + print "Shell failed! " + + return new_shell + + def write_shape(self, l_shells, filename, tol): + """ + Method to recreate a TopoDS_Shape associated to a geometric shape + after the modification of points of each Face. It + returns a TopoDS_Shape (Shape). + + :param l_shells: the list of shells after initial parsing + :param filename: the output filename + :param tol: tolerance on the surface creation after modification + :return: None + + """ + self.outfile = filename + # global compound containing multiple shells + global_compound_builder = BRep_Builder() + global_comp = OCC.TopoDS.TopoDS_Compound() + global_compound_builder.MakeCompound(global_comp) + + if self.check_topo == 0: + # cycle on shells (multiple objects) + shape_shells_explorer = TopExp_Explorer(self.shape + .Oriented(TopAbs_FORWARD), + TopAbs_SHELL) + ishell = 0 + + while shape_shells_explorer.More(): + per_shell = OCC.TopoDS.topods_Shell(shape_shells_explorer + .Current()) + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer(per_shell + .Oriented(TopAbs_FORWARD), + TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[ishell][iface][0], + l_shells[ishell][iface][1], + topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(ishell, itype)) + print "==============================================" + + ishell += 1 + shape_shells_explorer.Next() + + else: + # cycle on faces + # a local compound containing a shell + compound_builder = BRep_Builder() + comp = OCC.TopoDS.TopoDS_Compound() + compound_builder.MakeCompound(comp) + + # cycle on faces + faces_explorer = TopExp_Explorer(self.shape.Oriented(TopAbs_FORWARD), + TopAbs_FACE) + iface = 0 + while faces_explorer.More(): + topoface = OCC.TopoDS.topods.Face(faces_explorer.Current()) + newface = self.write_face(l_shells[0][iface][0], + l_shells[0][iface][1], + topoface, tol) + + # add face to compound + compound_builder.Add(comp, newface) + iface += 1 + faces_explorer.Next() + + new_shell = self.combine_faces(comp, 0.01) + itype = OCC.TopoDS.TopoDS_Shape.ShapeType(new_shell) + # add the new shell to the global compound + global_compound_builder.Add(global_comp, new_shell) + + print("Shell {0} of type {1} Processed ".format(0, itype)) + print "==============================================" + + + + self.write_shape_to_file(global_comp, self.outfile) + + def write_shape_to_file(self, shape, filename): + """ + Abstract method to write the 'shape' to the `filename`. + + Not implemented, it has to be implemented in subclasses. + """ + raise NotImplementedError(\ + "Subclass must implement abstract method " + \ + self.__class__.__name__ + ".write_shape_to_file") + + def plot(self, plot_file=None, save_fig=False): + """ + Method to plot a file. If `plot_file` is not given it plots `self.shape`. + + :param string plot_file: the filename you want to plot. + :param bool save_fig: a flag to save the figure in png or not. If True the + plot is not shown. + + :return: figure: matlplotlib structure for the figure of the chosen geometry + :rtype: matplotlib.pyplot.figure + """ + if plot_file is None: + shape = self.shape + plot_file = self.infile + else: + shape = self.load_shape_from_file(plot_file) + + stl_writer = StlAPI_Writer() + # Do not switch SetASCIIMode() from False to True. + stl_writer.SetASCIIMode(False) + stl_writer.Write(shape, 'aux_figure.stl') + + # Create a new plot + figure = pyplot.figure() + axes = mplot3d.Axes3D(figure) + + # Load the STL files and add the vectors to the plot + stl_mesh = mesh.Mesh.from_file('aux_figure.stl') + os.remove('aux_figure.stl') + axes.add_collection3d( + mplot3d.art3d.Poly3DCollection(stl_mesh.vectors / 1000)) + + # Get the limits of the axis and center the geometry + max_dim = np.array([ \ + np.max(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.max(stl_mesh.vectors[:, :, 1]) / 1000, \ + np.max(stl_mesh.vectors[:, :, 2]) / 1000]) + min_dim = np.array([ \ + np.min(stl_mesh.vectors[:, :, 0]) / 1000, \ + np.min(stl_mesh.vectors[:, :, 1]) / 1000, \ + np.min(stl_mesh.vectors[:, :, 2]) / 1000]) + + max_lenght = np.max(max_dim - min_dim) + axes.set_xlim( \ + -.6 * max_lenght + (max_dim[0] + min_dim[0]) / 2, \ + .6 * max_lenght + (max_dim[0] + min_dim[0]) / 2) + axes.set_ylim( \ + -.6 * max_lenght + (max_dim[1] + min_dim[1]) / 2, \ + .6 * max_lenght + (max_dim[1] + min_dim[1]) / 2) + axes.set_zlim( \ + -.6 * max_lenght + (max_dim[2] + min_dim[2]) / 2, \ + .6 * max_lenght + (max_dim[2] + min_dim[2]) / 2) + + # Show the plot to the screen + if not save_fig: + pyplot.show() + else: + figure.savefig(plot_file.split('.')[0] + '.png') + + return figure + + def show(self, show_file=None): + """ + Method to show a file. If `show_file` is not given it plots `self.shape`. + + :param string show_file: the filename you want to show. + """ + if show_file is None: + shape = self.shape + else: + shape = self.load_shape_from_file(show_file) + + display, start_display, __, __ = init_display() + display.FitAll() + display.DisplayShape(shape, update=True) + + # Show the plot to the screen + start_display()