def _bspline_restrict(solid, tol): """ Attempt to re-fit OpenVSP surfaces with more continuity. """ # Use dmax=1 because that was only way to get the tool actually refit the # surfaces other than another surface where the multiplicity equaled the # degree. Not sure why the tool operates this way. logger.info('\tApplying ShapeBSplineRestriction tool...') tool = ShapeBSplineRestriction(solid, dmax=1, tol3d=tol) if not tool.is_done: logger.info('Method unsuccessful. Using original solid.') return solid # Get new shape and solid new_solid = tool.modified_shape(solid) # Limit/fix tolerance FixShape.limit_tolerance(new_solid) tol = new_solid.tol_avg if not CheckShape(new_solid).is_valid: logger.info('Shape invalid. Using original solid.') return solid logger.info('\tMethod successful with surface error: {}'.format( tool.error_surface)) logger.info('\tNew shape tolerance: {}'.format(tol)) return new_solid
def __init__(self, group=None, precision=None, min_tol=None, max_tol=None): group = GroupAPI.get_group(group) if not isinstance(group, Group): raise TypeError('Could not find group.') parts = group.get_parts() compound = group.get_shape() fix = FixShape(compound, precision, min_tol, max_tol) for part in parts: new_shape = fix.apply(part.shape) part.set_shape(new_shape)
def fix(self, precision=None, min_tol=None, max_tol=None, context=None, include_subgroup=True): """ Attempt to fix the shape of the part using :class:`.FixShape`. :param float precision: Basic precision value. :param float min_tol: Minimum tolerance. :param float max_tol: Maximum tolerance. :param context: The context shape or group. :type context: afem.topology.entities.Shape or afem.structure.entities.Group or str :param bool include_subgroup: Option to recursively include parts from any subgroups. :return: None. """ if context is not None: if not isinstance(context, Shape): context = GroupAPI.get_shape(context, include_subgroup) new_shape = FixShape(self._shape, precision, min_tol, max_tol, context).shape self.set_shape(new_shape)
def set_tolerance(group=None, tol=1.0e7): """ Enforce tolerance on the given group. :param group: The group. If ``None`` then the active group is used. :type group: str or afem.structure.group.Group or None :param float tol: The tolerance. :return: None. :raise TypeError: If an :class:`.Group` instance is not found. """ group = GroupAPI.get_group(group) if not isinstance(group, Group): raise TypeError('Could not find group.') shape = group.get_shape() return FixShape.set_tolerance(shape, tol)
def limit_tolerance(group=None, tol=1.0e-7): """ Limit tolerances for the group shapes. :param group: The group. If ``None`` then the active group is used. :type group: str or afem.structure.group.Group or None :param float tol: Target tolerance. :return: *True* if at least one tolerance of a sub-shape was modified. :rtype: bool :raise TypeError: If an :class:`.Group` instance is not found. """ group = GroupAPI.get_group(group) if not isinstance(group, Group): raise TypeError('Could not find group.') shape = group.get_shape() return FixShape.limit_tolerance(shape, tol)
def _build_solid(compound, divide_closed): """ Try to build a solid from the OpenVSP compound of faces. :param afem.topology.entities.Compound compound: The compound. :param bool divide_closed: Option to divide closed faces. :return: The solid. :rtype: afem.topology.entities.Solid """ # Get all the faces in the compound. The surfaces must be split. Discard # any with zero area. faces = [] for face in compound.faces: area = SurfaceProps(face).area if area > 1.0e-7: faces.append(face) # Replace any planar B-Spline surfaces with planes. non_planar_faces = [] planar_faces = [] for f in faces: srf = f.surface try: pln = srf.as_plane() if pln: w = f.outer_wire # Fix the wire because they are usually degenerate edges in # the planar end caps. builder = BRepBuilderAPI_MakeWire() for e in w.edges: if LinearProps(e).length > 1.0e-7: builder.Add(e.object) w = builder.Wire() fix = ShapeFix_Wire() fix.Load(w) fix.SetSurface(pln.object) fix.FixReorder() fix.FixConnected() fix.FixEdgeCurves() fix.FixDegenerated() w = Wire(fix.WireAPIMake()) fnew = Face.by_wire(w) planar_faces.append(fnew) else: non_planar_faces.append(f) except RuntimeError: logger.info('Failed to check for planar face...') non_planar_faces.append(f) # Make a compound of the faces shape = Compound.by_shapes(non_planar_faces + planar_faces) # Split closed faces if divide_closed: shape = DivideClosedShape(shape).shape # Sew shape sewn_shape = SewShape(shape).sewed_shape if isinstance(sewn_shape, Face): sewn_shape = sewn_shape.to_shell() # Attempt to unify planar domains shell = UnifyShape(sewn_shape).shape # Make solid if not isinstance(shell, Shell): logger.info('\tA valid shell was not able to be generated.') check = CheckShape(shell) if not check.is_valid: logger.info('\tShape errors:') check.log_errors() return shell, check.invalid_shapes solid = Solid.by_shell(shell) # Limit tolerance FixShape.limit_tolerance(solid) # Check the solid and attempt to fix invalid = [] check = CheckShape(solid) if not check.is_valid: logger.info('\tFixing the solid...') solid = FixShape(solid).shape check = CheckShape(solid) if not check.is_valid: logger.info('\t...solid could not be fixed.') logger.info('\tShape errors:') check.log_errors() failed = check.invalid_shapes invalid += failed else: tol = solid.tol_avg logger.info( '\tSuccessfully generated solid with tolerance={}'.format(tol)) return solid, invalid