def test_bounds_sphere_boundingbox_middle(): r"""Test the determination of the bounding box middle""" # occutils.mesh.mesh(box) bb = BoundingBox(sphere_) assert bb.centre.X() < tol / 10. assert bb.centre.Y() < tol / 10. assert bb.centre.Z() < tol / 10.
def test_bounds_box(): r"""Test the bounding box on a box shape""" # mesh(box) bb = BoundingBox(box_) assert box_dim_x <= bb.x_span < box_dim_x + 2.001 * tol assert box_dim_y <= bb.y_span < box_dim_y + 2.001 * tol assert box_dim_z <= bb.z_span < box_dim_z + 2.001 * tol
def convert_brep_file(brep_filename, target_folder, remove_original=True): r"""Convert a BREP file (.brep, .brp) for web display Parameters ---------- brep_filename : str Full path to the BREP 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) shape = BrepImporter(brep_filename).shape converted_filename = _conversion_filename(brep_filename, target_folder, 0) converted_basenames = [basename(converted_filename)] _convert_shape(shape, converted_filename) _write_descriptor( BoundingBox(shape).max_dimension, converted_basenames, _descriptor_filename(target_folder, basename(brep_filename))) if remove_original is True: remove(brep_filename)
def test_bounds_sphere(): r"""Test the bounding box on a sphere""" # mesh(box) bb = BoundingBox(sphere_) assert 2 * sphere_radius <= bb.x_span < 2 * sphere_radius + 2.001 * tol assert 2 * sphere_radius <= bb.y_span < 2 * sphere_radius + 2.001 * tol assert 2 * sphere_radius <= bb.z_span < 2 * sphere_radius + 2.001 * tol
def mesh(shape, factor=4000., use_min_dim=False): r"""Mesh a shape Parameters ---------- shape : OCC.TopoDS.TopoDS_Shape Shape to mesh factor : float The higher, the finer the mesh use_min_dim : bool (optional) Use minimum bounding box dimension to compute the linear deflection. The default is False (i.e. use max dimension) This is useful for long and thin objects where using the max dimension would result in a very coarse linear deflection in the other directions. """ bb = BoundingBox(shape) if use_min_dim: linear_deflection = bb.min_dimension / factor logger.info("Linear deflection : %f" % linear_deflection) OCC.BRepMesh.BRepMesh_IncrementalMesh(shape, linear_deflection) else: linear_deflection = bb.max_dimension / factor logger.info("Linear deflection : %f" % linear_deflection) OCC.BRepMesh.BRepMesh_IncrementalMesh(shape, linear_deflection)
def test_with_box(): r"""Test the bounding box workaround with a box""" dx = 10.0 dy = 20.0 dz = 30.0 # Make a box with a corner at 0,0,0 and the other dx,dy,dz box = BRepPrimAPI_MakeBox(dx, dy, dz).Shape() bb = BoundingBox(box) increment = 0.01 bbw_x_min = real_bb_position("X", "MIN", bb.x_min, box, increment=increment) bbw_x_max = real_bb_position("X", "MAX", bb.x_max, box, increment=increment) bbw_y_min = real_bb_position("Y", "MIN", bb.y_min, box, increment=increment) bbw_y_max = real_bb_position("Y", "MAX", bb.y_max, box, increment=increment) bbw_z_min = real_bb_position("Z", "MIN", bb.z_min, box, increment=increment) bbw_z_max = real_bb_position("Z", "MAX", bb.z_max, box, increment=increment) assert bb.x_min < 0. assert bb.x_max > dx assert bb.y_min < 0. assert bb.y_max > dy assert bb.z_min < 0. assert bb.z_max > dz assert bbw_x_min <= 0. assert abs(bbw_x_min) <= increment assert bbw_x_max >= dx assert abs(bbw_x_max - dx) <= increment assert bbw_y_min <= 0. assert abs(bbw_y_min) <= increment assert bbw_y_max >= dy assert abs(bbw_y_max - dy) <= increment assert bbw_z_min <= 0. assert abs(bbw_z_min) <= increment assert bbw_z_max >= dz assert abs(bbw_z_max - dz) <= increment
def point_in_boundingbox(shape, pnt, tolerance=OCCUTILS_DEFAULT_TOLERANCE): r"""Is pnt inside the bounding box of solid? This is a much speedier test than checking the TopoDS_Solid Parameters ---------- shape : TopoDS_Shape pnt : OCC.gp.gp_Pnt tolerance : float Returns ------- bool True if pnt lies in boundingbox, False otherwise """ return not BoundingBox(shape, tolerance).bnd_box.IsOut(pnt)
def test_with_sphere(): r"""Test the bounding box workaround with a sphere""" radius = 10.0 sphere = BRepPrimAPI_MakeSphere(radius).Shape() bb = BoundingBox(sphere) increment = 0.01 bbw_x_min = real_bb_position("X", "MIN", bb.x_min, sphere, increment=increment) bbw_x_max = real_bb_position("X", "MAX", bb.x_max, sphere, increment=increment) bbw_y_min = real_bb_position("Y", "MIN", bb.y_min, sphere, increment=increment) bbw_y_max = real_bb_position("Y", "MAX", bb.y_max, sphere, increment=increment) bbw_z_min = real_bb_position("Z", "MIN", bb.z_min, sphere, increment=increment) bbw_z_max = real_bb_position("Z", "MAX", bb.z_max, sphere, increment=increment) assert bb.x_min < -radius assert bb.x_max > radius assert bb.y_min < -radius assert bb.y_max > radius assert bb.z_min < -radius assert bb.z_max > radius assert bbw_x_min <= - radius assert abs(bbw_x_min - (-radius)) <= increment assert bbw_x_max >= radius assert abs(bbw_x_max - radius) <= increment assert bbw_y_min <= - radius assert abs(bbw_y_min - (-radius)) <= increment assert bbw_y_max >= radius assert abs(bbw_y_max - radius) <= increment assert bbw_z_min <= - radius assert abs(bbw_z_min - (-radius)) <= increment assert bbw_z_max >= radius assert abs(bbw_z_max - radius) <= increment
def convert_py_file_part(py_filename, target_folder, remove_original=True): r"""Convert an OsvCad Python file that contains a part for web display The Python file contains the definition of a part. Parameters ---------- py_filename : str Full path to the Python 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 Raises ------ ValueError if not a part definition """ if not isdir(target_folder): mkdir(target_folder) part = Part.from_py_script(py_filename) shape = part.node_shape.shape converted_filename = _conversion_filename(py_filename, target_folder, 0) converted_basenames = [basename(converted_filename)] _convert_shape(shape, converted_filename) _write_descriptor(BoundingBox(shape).max_dimension, converted_basenames, _descriptor_filename(target_folder, basename(py_filename))) if remove_original is True: remove(py_filename)
def convert_stepzip_file(stepzip_filename, target_folder, remove_original=True): r"""Convert an OsvCad Stepzip file for web display A Stepzip file contains a STEP geometry file and an anchors definition file zipped together Parameters ---------- stepzip_filename : str Full path to the Stepzip 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) part = Part.from_stepzip(stepzip_filename) shape = part.node_shape.shape converted_filename = _conversion_filename(stepzip_filename, target_folder, 0) converted_basenames = [basename(converted_filename)] _convert_shape(shape, converted_filename) _write_descriptor( BoundingBox(shape).max_dimension, converted_basenames, _descriptor_filename(target_folder, basename(stepzip_filename))) if remove_original is True: remove(stepzip_filename)
def convert_iges_file(iges_filename, target_folder, remove_original=True): r"""Convert an IGES file (.iges, .igs) for web display Parameters ---------- iges_filename : str Full path to IGES 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 = IgesImporter(iges_filename) shapes = importer.shapes max_dim = BoundingBox(importer.compound).max_dimension for i, shape in enumerate(shapes): converted_filename = _conversion_filename(iges_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(iges_filename))) if remove_original is True: remove(iges_filename)
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")
def convert_freecad_file(freecad_filename, target_folder, remove_original=True): r"""Convert a FreeCAD file (.fcstd) for web display Parameters ---------- freecad_filename : str Full path to FreeCAD 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 """ logger.info("Starting FreeCAD conversion") folder_unzipping = join(target_folder, splitext(basename(freecad_filename))[0]) extract_fcstd(freecad_filename, folder_unzipping) name_file_visibility_tuples = \ name_file_visibility_from_unzipping_folder(folder_unzipping) extremas = [] converted_basenames = [] for i, (n, f, v) in enumerate(name_file_visibility_tuples): if v is True: brep_filename = "%s/%s" % (folder_unzipping, f) converted_filename = _conversion_filename(brep_filename, target_folder, i) converted_basenames.append(basename(converted_filename)) try: importer = BrepImporter(brep_filename) extremas.append(BoundingBox(importer.shape).as_tuple) _convert_shape(importer.shape, converted_filename) except (RuntimeError, AssertionError): # An AssertionError is raised if one of the BREPs contained # in the FCSTD file contains a NULL shape logger.error("RuntimeError for %s" % brep_filename) x_min = min([extrema[0] for extrema in extremas]) y_min = min([extrema[1] for extrema in extremas]) z_min = min([extrema[2] for extrema in extremas]) x_max = max([extrema[3] for extrema in extremas]) y_max = max([extrema[4] for extrema in extremas]) z_max = max([extrema[5] for extrema in extremas]) max_dim = max([x_max - x_min, y_max - y_min, z_max - z_min]) _write_descriptor(max_dim, converted_basenames, _descriptor_filename(target_folder, basename(freecad_filename))) # Cleanup of files that are not needed anymore if remove_original is True: remove(freecad_filename) rmtree(folder_unzipping, ignore_errors=True)
def _characteristic_dimension(shape): from aocutils.analyze.bounds import BoundingBox bb = BoundingBox(shape) return (bb.x_span + bb.y_span + bb.z_span) / 3.
#!/usr/bin/env python # coding: utf-8 r"""Hull FFD example with IGES""" from aocxchange.iges import IgesImporter from aocutils.analyze.bounds import BoundingBox import pygem as pg from pygem.utils import write_bounding_box # Bounding box hull_shape_compound = \ IgesImporter(filename="./example_hull_iges/SYSSER01_Z0WL.igs").compound bb = BoundingBox(hull_shape_compound) print("X min : %f" % bb.x_min) print("X max : %f" % bb.x_max) print("Y min : %f" % bb.y_min) print("Y max : %f" % bb.y_max) print("Z min : %f" % bb.z_min) print("Z max : %f" % bb.z_max) assert abs(abs(bb.y_min) - abs(bb.y_max)) <= 1e-6 # Initial file loading and visualization iges_handler = pg.igeshandler.IgesHandler() mesh_points = iges_handler.parse('./example_hull_iges/SYSSER01_Z0WL.igs') iges_handler.check_topology() # Display the unmodified hull in 2 possible ways