Ejemplo n.º 1
0
def test_step_exporter_overwrite(box_shape):
    r"""Happy path with a subclass of TopoDS_Shape"""
    filename = path_from_file(__file__, "./models_out/box.stp")
    exporter = StepExporter(filename)
    solid = shape_to_topology(box_shape)
    assert isinstance(solid, TopoDS_Solid)
    exporter.add_shape(solid)
    exporter.write_file()
    initial_timestamp = os.path.getmtime(filename)
    assert os.path.isfile(filename)

    # read the written box.stp
    importer = StepImporter(filename)
    topo_compound = Topo(importer.compound, return_iter=False)
    assert topo_compound.number_of_faces == 6
    assert len(topo_compound.faces) == 6
    assert topo_compound.number_of_edges == 12

    # add a sphere and write again with same exporter
    sphere = BRepPrimAPI_MakeSphere(10)
    exporter.add_shape(sphere.Shape())
    exporter.write_file()  # this creates a file with a box and a sphere
    intermediate_timestamp = os.path.getmtime(filename)
    assert intermediate_timestamp > initial_timestamp

    # check that the file contains the box and the sphere
    importer = StepImporter(filename)

    # 6 from box + 1 from sphere
    assert len(Topo(importer.compound, return_iter=False).faces) == 7

    assert len(Topo(importer.compound, return_iter=False).solids) == 2

    # create a new exporter and overwrite with a box only
    filename = path_from_file(__file__, "./models_out/box.stp")
    exporter = StepExporter(filename)
    solid = shape_to_topology(box_shape)
    exporter.add_shape(solid)
    exporter.write_file()
    assert os.path.isfile(filename)
    last_timestamp = os.path.getmtime(filename)
    assert last_timestamp > intermediate_timestamp

    # check the file only contains a box
    importer = StepImporter(filename)

    # 6 from box
    assert len(Topo(importer.compound, return_iter=False).faces) == 6

    assert len(Topo(importer.compound, return_iter=False).solids) == 1
Ejemplo n.º 2
0
def view_topology_with_aocutils(step_filename):
    r"""View the STEP file contents in the aocutils wx viewer.

    The aocutils wx viewer is good to visualize topology.

    Parameters
    ----------
    step_filename : str
        Path to the STEP file

    """

    importer = StepImporter(filename=step_filename)

    class MyFrame(wx.Frame):
        r"""Frame for testing"""
        def __init__(self):
            wx.Frame.__init__(self, None, -1)
            self.p = Wx3dViewer(self)
            for shape in importer.shapes:
                self.p.display_shape(shape)
            self.Show()

    app = wx.App()
    frame = MyFrame()
    app.SetTopWindow(frame)
    app.MainLoop()
Ejemplo n.º 3
0
def anchorable_part_from_stepzip(stepzip_filepath):
    r"""Create an anchorable part (AnchorablePart instance) from a stepzip file

    Parameters
    ----------
    stepzip_filepath : str
        Path to the stepzip file

    Returns
    -------
    AnchorablePart

    """
    if stepzip_filepath in cache:
        shape, stepfile_path, anchors_dict, properties = cache[
            stepzip_filepath]
    else:
        stepfile_path, part_data_file_path = extract_stepzip(stepzip_filepath)

        step_imp = StepImporter(stepfile_path)
        shape = step_imp.compound

        anchors_dict, properties = read_part_data(part_data_file_path)

        cache[stepzip_filepath] = (shape, stepfile_path, anchors_dict,
                                   properties)

    anchors_list = anchors_dict_to_list(anchors_dict)

    return AnchorablePart(shape=shape,
                          name=splitext(basename(stepfile_path))[0],
                          anchors=anchors_list,
                          properties=properties)
Ejemplo n.º 4
0
def test_step_importer_happy_path():
    r"""happy path"""
    importer = StepImporter(
        path_from_file(__file__, "./models_in/aube_pleine.stp"))
    assert isinstance(importer.compound, TopoDS_Compound)
    assert isinstance(importer.shapes, list)
    for shape in importer.shapes:
        assert isinstance(shape, TopoDS_Shape)
    assert len(importer.shapes) == 1
Ejemplo n.º 5
0
def test_step_importer_2_boxes():
    r"""Import an step file containing 2 distinct boxes and test topology"""
    importer = StepImporter(
        path_from_file(__file__, "./models_in/2_boxes_203.stp"))
    assert len(importer.shapes) == 1
    assert importer.shapes[0].ShapeType() == TopAbs_COMPOUND

    topo = Topo(importer.shapes[0])
    assert topo.number_of_compounds == 1
    assert topo.number_of_comp_solids == 0
    assert topo.number_of_solids == 2
    assert topo.number_of_shells == 2
Ejemplo n.º 6
0
def test_step_importer_happy_topology():
    r"""import step file containing a box and test topology"""
    importer = StepImporter(path_from_file(__file__,
                                           "./models_in/box_203.stp"))
    assert len(importer.shapes) == 1

    assert isinstance(importer.shapes[0], TopoDS_Shape)
    assert importer.shapes[0].ShapeType() == TopAbs_SOLID

    topo = Topo(importer.shapes[0])
    assert topo.number_of_compounds == 0
    assert topo.number_of_comp_solids == 0
    assert topo.number_of_solids == 1
    assert topo.number_of_shells == 1
Ejemplo n.º 7
0
def test_better_bounds_complex_shape():
    r"""Test BetterBoundingBox on a complex shape"""

    hullshape = StepImporter(filename=os.path.join(os.path.dirname(__file__),
                                                   "test_files/G10.stp")).shapes[0]
    # From Rhinoceros bounding box
    # min = 0.000,-82.263,-52.092
    # max = 990.000,82.263,102.210    in World coordinates
    # dimensions = 990.000,164.527,154.302
    #
    # z max has to be replaced by 102.20984 for the test to pass
    tolerance = 0.01
    bbb = BetterBoundingBox(hullshape, tol=tolerance)
    assert -tolerance <= bbb.x_min <= 0.
    assert 990. <= bbb.x_max <= 990. + tolerance

    assert -82.263 - tolerance <= bbb.y_min <= -82.263
    assert 82.263 <= bbb.y_max <= 82.263 + tolerance

    assert -52.092 - tolerance <= bbb.z_min <= -52.092
    assert 102.20984 <= bbb.z_max <= 102.210 + tolerance
Ejemplo n.º 8
0
def handle_cad_file_open():
    r"""Handle the logic of cad file opening

    Returns
    -------
    path : str
    type_ : str
        iges, step, stl brep
    shapes: list[OCC.TopoDS.TopoDS_Shape]

    """
    with OpenCadDialog() as open_cad_file_dialog:
        if open_cad_file_dialog.ShowModal() == wx.ID_OK:

            # cast the path from the FileDialog to st to avoid
            # TypeError: in method 'XSControl_Reader_ReadFile',
            #                                  argument 2 of type 'char const *'
            path = str(open_cad_file_dialog.GetPath())
            extension = extract_file_extension(path)

            if extension.lower() in step_extensions:
                type_ = "step"
                shapes = StepImporter(path).shapes
            elif extension.lower() in iges_extensions:
                type_ = "iges"
                shapes = IgesImporter(path).shapes
            elif extension.lower() in stl_extensions:
                type_ = "stl"
                shapes = list()
                shapes.append(StlImporter(path).shape)
            elif extension.lower() in brep_extensions:
                type_ = "brep"
                shapes = list()
                shapes.append(BrepImporter(path).shape)
            else:
                raise ValueError("File extension indicates a file type that "
                                 "is not supported")
            # return filepath + type (iges ...) + list of shapes
            return path, type_, shapes
        else:  # cancel button
            return None, None, None
Ejemplo n.º 9
0
def convert_step_file(step_filename, target_folder, remove_original=True):
    r"""Convert a STEP file (.step, .stp) for web display

    Parameters
    ----------
    step_filename : str
        Full path to the STEP file
    target_folder : str
        Full path to the target folder for the conversion
    remove_original : bool
        Should the input file be deleted after conversion?
        It should be deleted on a web platform to save disk space, but, for
        testing, it might be useful not to delete it.

    Returns
    -------
    Nothing, it is a procedure

    """
    if not isdir(target_folder):
        mkdir(target_folder)
    converted_basenames = []
    importer = StepImporter(step_filename)
    shapes = importer.shapes
    max_dim = BoundingBox(importer.compound).max_dimension
    for i, shape in enumerate(shapes):
        converted_filename = _conversion_filename(step_filename, target_folder,
                                                  i)
        _convert_shape(shape, converted_filename)
        converted_basenames.append(basename(converted_filename))

    _write_descriptor(
        max_dim, converted_basenames,
        _descriptor_filename(target_folder, basename(step_filename)))
    if remove_original is True:
        remove(step_filename)
from OCC.Display.SimpleGui import init_display

from aocutils.display.topology import solids, edges
from aocutils.display.defaults import backend

from aocxchange.step import StepImporter
from corelib.core.files import path_from_file

logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s :: %(levelname)6s :: %(module)20s :: '
                           '%(lineno)3d :: %(message)s')

backend = backend
display, start_display, add_menu, add_function_to_menu = init_display(backend)

filename = path_from_file(__file__, "./models_in/step/aube_pleine.stp")
step_importer = StepImporter(filename)

# step_importer.read_file() -> automatic read_file !!

print("Nb shapes: %i" % len(step_importer.shapes))
for shape in step_importer.shapes:
    print(shape.ShapeType())  # 2 -> solid
# print("number_of_shapes: %i" % step_importer.number_of_shapes)  # 0 ??
# display.DisplayShape(step_importer.shapes)
solids(display, step_importer.shapes[0], transparency=0.8)
edges(display, step_importer.shapes[0])
display.FitAll()
display.View_Iso()
start_display()
Ejemplo n.º 11
0
    def on_selected_change(self, change):
        """Callback function for listener"""

        # TODO: investigate why importing Part
        # at top of file causes an app crash
        from cadracks_core.model import Part

        logger.debug("Selection changed")

        sel = self.model.selected

        logger.debug("sel : %s" % sel)

        if not isdir(sel):
            # what extension ?
            ext = splitext(sel)[1].lower()

            logger.info("File extension : %s" % ext)

            if ext == ".py":
                with open(sel) as f:
                    content = f.read()

                if is_valid_python(content) is True:
                    busy_info = wx.BusyInfo(
                        "Loading Python defined geometry ...")
                    try:
                        module_ = imp.load_source(sel, sel)

                        has_shape = hasattr(module_, "__shape__")
                        has_shapes = hasattr(module_, "__shapes__")
                        has_anchors = hasattr(module_, "__anchors__")
                        has_properties = hasattr(module_, "__properties__")
                        has_assembly = hasattr(module_, "__assembly__")
                        has_assemblies = hasattr(module_, "__assemblies__")

                        self.erase_all()

                        if has_shapes is True:
                            logger.info("%s has shapeS" % sel)
                            for i, shape in enumerate(module_.__shapes__):
                                self.display_shape(shape,
                                                   color_=colour_wx_to_occ(
                                                       color_from_sequence(
                                                           i, "colors")))

                        elif has_assembly is True:
                            logger.info("%s has assembly" % sel)
                            try:
                                self.display_assembly(module_.__assembly__)
                                self.viewer_display.FitAll()
                            except KeyError as ke:
                                self.erase_all()
                                logger.exception(ke)

                        elif has_assemblies is True:
                            logger.info("%s has assemblies" % sel)
                            try:
                                self.display_assemblies(module_.__assemblies__)
                                self.viewer_display.FitAll()
                            except KeyError as ke:
                                self.erase_all()
                                logger.exception(ke)
                        # elif has_shape is True and has_anchors is True:
                        elif has_shape is True:
                            if has_anchors is False:
                                logger.info("%s has shape" % sel)
                                self.display_shape(module_.__shape__)
                            else:
                                logger.info("%s has shape and anchors" % sel)
                                p = anchorable_part_from_py_script(sel)
                                self.display_part(p)
                            self.viewer_display.FitAll()
                        else:
                            self.erase_all()
                            logger.warning("Nothing to display")
                    except Exception as e:
                        logger.error(str(e))
                    finally:
                        del busy_info
                else:  # the file is not a valid Python file
                    logger.warning("Not a valid python file")
                    self.erase_all()

            elif ext in [".step", ".stp"]:
                self.erase_all()
                busy_info = wx.BusyInfo("Loading STEP ...")
                try:
                    shapes = StepImporter(sel).shapes
                    logger.info("%i shapes in %s" % (len(shapes), sel))
                    for shape in shapes:
                        color_255 = (255, 255, 255)
                        self.display_shape(shape,
                                           color_=colour_wx_to_occ(color_255),
                                           transparency=0.1)
                        self.viewer_display.FitAll()
                except Exception as e:
                    logger.error(str(e))
                finally:
                    del busy_info

            elif ext in [".iges", ".igs"]:
                self.erase_all()
                busy_info = wx.BusyInfo("Loading IGES ...")
                try:
                    shapes = IgesImporter(sel).shapes
                    logger.info("%i shapes in %s" % (len(shapes), sel))
                    for shape in shapes:
                        color_255 = (51, 255, 255)
                        self.display_shape(shape,
                                           color_=colour_wx_to_occ(color_255),
                                           transparency=0.1)
                        self.viewer_display.FitAll()
                except Exception as e:
                    logger.error(str(e))
                finally:
                    del busy_info

            elif ext == ".stl":
                self.erase_all()
                busy_info = wx.BusyInfo("Loading STL ...")
                try:
                    shape = StlImporter(sel).shape
                    color_255 = (0, 255, 0)
                    self.display_shape(shape,
                                       color_=colour_wx_to_occ(color_255),
                                       transparency=0.1)
                    self.viewer_display.FitAll()
                except Exception as e:
                    logger.error(str(e))
                finally:
                    del busy_info

            elif ext == ".json":  # parts library
                self.erase_all()

                is_library_file = False

                with open(sel) as json_file:
                    if "metadata" in json_file.read():
                        is_library_file = True

                if is_library_file is True:
                    busy_info = wx.BusyInfo("Loading parts library ...")
                    try:
                        with open(sel) as json_file:
                            json_file_content = json.load(json_file)
                            # print(json_file_content["data"].keys())
                            # find the biggest bounding box
                            biggest_bb = [0, 0, 0]
                            # smallest_bb = [0, 0, 0]
                            for i, k in enumerate(
                                    json_file_content["data"].keys()):
                                library_part = anchorable_part_from_library(
                                    sel, k)
                                bb = BoundingBox(library_part.shape)
                                if bb.x_span > biggest_bb[0]:
                                    biggest_bb[0] = bb.x_span
                                # if bb.x_span < smallest_bb[0]:
                                #     smallest_bb[0] = bb.x_span
                                if bb.y_span > biggest_bb[1]:
                                    biggest_bb[1] = bb.y_span
                                # if bb.y_span < smallest_bb[1]:
                                #     smallest_bb[1] = bb.y_span
                                if bb.z_span > biggest_bb[2]:
                                    biggest_bb[2] = bb.z_span
                                # if bb.z_span < smallest_bb[2]:
                                #     smallest_bb[2] = bb.y_span
                            biggest_dimension = max(biggest_bb)
                            # smallest_dimension = min(smallest_bb)

                            nb_per_row = int(
                                math.sqrt(len(
                                    json_file_content["data"].keys())))

                            for i, k in enumerate(
                                    json_file_content["data"].keys()):
                                library_part = anchorable_part_from_library(
                                    sel, k)
                                x_pos = biggest_dimension * 2 * (i %
                                                                 nb_per_row)
                                y_pos = biggest_dimension * 2 * (i //
                                                                 nb_per_row)

                                # Translate using an AnchorTransformation
                                library_part.add_matrix_generator(
                                    AnchorTransformation(
                                        Anchor(p=(0, 0, 0),
                                               u=(1, 0, 0),
                                               v=(0, 1, 0),
                                               name='a'),
                                        Anchor(p=(x_pos, y_pos, 0),
                                               u=(1, 0, 0),
                                               v=(0, 1, 0),
                                               name='b')))
                                self.display_part(library_part,
                                                  transparency=0.2,
                                                  anchor_names_height=10)
                                pnt_message = gp_Pnt(
                                    x_pos + biggest_dimension / 5,
                                    y_pos + biggest_dimension / 5, 0)
                                self.display_message(pnt_message,
                                                     k,
                                                     message_color=(1, 1, 1),
                                                     height=20)
                                self.viewer_display.FitAll()
                    except Exception as e:
                        logger.error(str(e))
                    finally:
                        del busy_info
            elif ext == ".stepzip":
                self.erase_all()
                busy_info = wx.BusyInfo("Loading STEPZIP ...")
                try:
                    self.display_part(anchorable_part_from_stepzip(sel),
                                      transparency=0.2)
                    self.viewer_display.FitAll()
                except Exception as e:
                    logger.error(str(e))
                finally:
                    del busy_info
            else:
                logger.error("File has an extension %s that is not "
                             "handled by the 3D panel" % ext)
                self.erase_all()

        else:  # a directory is selected
            self.erase_all()

        self.Layout()

        logger.debug("code change detected in 3D panel")
Ejemplo n.º 12
0
def test_step_importer_wrong_file_content():
    r"""wrong file content"""
    with pytest.raises(StepFileReadException):
        StepImporter(path_from_file(__file__, "./models_in/empty.stp"))
Ejemplo n.º 13
0
def test_step_importer_wrong_extension():
    r"""wrong file format (i.e. trying to read a iges file
    with step importer)"""
    with pytest.raises(IncompatibleFileFormatException):
        filename = path_from_file(__file__, "./models_in/aube_pleine.iges")
        StepImporter(filename)
Ejemplo n.º 14
0
def test_step_importer_wrong_path():
    r"""Wrong filename"""
    with pytest.raises(FileNotFoundError):
        StepImporter("C:/stupid-filename.bad_extension")
Ejemplo n.º 15
0
def import_step():
    """
    Imports a STEP file.
    """
    my_importer = StepImporter("./models_in/step/box_203.stp")
    print(my_importer.shapes)
Ejemplo n.º 16
0
def step_to_stl(step_file_path,
                stl_file_path,
                scale=1.,
                factor=4000.,
                use_min_dim=False,
                ascii_mode=True,
                multi_shape_mode=ONE_SHAPE_PER_FILE):
    r"""Convert a STEP file to a STL file

    Parameters
    ----------
    step_file_path : str
        Path to the input STEP file
    stl_file_path : str
        Path to the output STL file
    factor : float
        Meshing factor, optional (default is 4000.)
        The higher, the finer the mesh and the bigger the resulting file
    use_min_dim : bool, optional (default is False)
        Use the minimum dimension of the shape as a base for meshing
        This is useful for shapes with a high aspect ratio
    ascii_mode : bool, optional (default is True)
        Write STL in ascii mode if True, in binary mode if False
    multi_shape_mode : int, optional (default is 0 (ONE_SHAPE_PER_FILE)
        Mode to use in case there is more than 1 shape in the STEP file

    Returns
    -------
    List of STL files creates, total number of shapes
    If there is only 1 file in the liste and more than 1 shape, it is a STL with
    multiple entities

    """
    def shape_to_stl(shape_, stl_file_, scale, ascii_mode_, factor_,
                     use_min_dim_):
        r"""Write a single shape to an STL file
    
        Parameters
        ----------
        shape_
        stl_file_
        ascii_mode_
        factor_
        use_min_dim_

        """
        exporter = StlExporter(filename=stl_file_, ascii_mode=ascii_mode_)

        shape_ = scale_uniform(shape_, (0, 0, 0), scale, False)

        # Must mesh ! Otherwise the exporter does not write anything!
        mesh(shape_, factor=factor_, use_min_dim=use_min_dim_)

        exporter.set_shape(shape_)
        exporter.write_file()

    # Read STEP
    shapes = StepImporter(filename=step_file_path).shapes

    if len(shapes) == 0:
        msg = "The STEP file contains no shape"
        logger.error(msg)
        raise ValueError(msg)

    elif len(shapes) == 1:  # 99.9 % of practical cases
        shape_to_stl(shapes[0], stl_file_path, scale, ascii_mode, factor,
                     use_min_dim)
        # assert isfile(stl_file_path)
        if not isfile(stl_file_path):
            msg = "%s was not created" % stl_file_path
            logger.error(msg)
            raise ValueError(msg)
        return [stl_file_path], 1

    elif len(shapes) > 1:
        if multi_shape_mode == ONE_SHAPE_PER_FILE:
            f, e = os.path.splitext(stl_file_path)
            partial_files = list()
            for i, shape in enumerate(shapes):
                filename = f + "_" + str(i) + e
                shape_to_stl(shape, filename, scale, ascii_mode, factor,
                             use_min_dim)
                partial_files.append(filename)
            return partial_files, len(shapes)

        elif multi_shape_mode == ALL_SHAPES_IN_ONE_FILE:
            partial_files = list
            for i, shape in enumerate(shapes):
                filename = f + "_" + str(i) + e
                shape_to_stl(shape, filename, scale, ascii_mode, factor,
                             use_min_dim)
                partial_files.append(filename)
            with open(stl_file_path, "w") as stl_file:
                for partial_file in partial_files:
                    with open(partial_file) as pf:
                        content = pf.readlines()
                        stl_file.writelines(content)
            for partial_file in partial_files:
                os.remove(partial_file)
            return [stl_file_path], len(shapes)

        else:
            msg = "Unknown mode for multi shape STEP conversion to STL"
            raise ValueError(msg)