def __init__(self, old_shapes, tool): reshape = ShapeBuild_ReShape() self._new_shapes = TopTools_DataMapOfShapeShape() index_map = TopTools_IndexedMapOfShape() for old_shape in old_shapes: # Old shapes shapes = old_shape.faces if not shapes: shapes = old_shape.edges if not shapes: shapes = old_shape.vertices if not shapes: continue # Delete and replace for shape in shapes: # Deleted if tool.is_deleted(shape): reshape.Remove(shape.object) continue # Modified considering shapes already used mod_shapes = tool.modified(shape) replace_shapes = [] for mod_shape in mod_shapes: if index_map.Contains(mod_shape.object): continue replace_shapes.append(mod_shape) index_map.Add(mod_shape.object) if replace_shapes: new_shape = Compound.by_shapes(replace_shapes) reshape.Replace(shape.object, new_shape.object) new_shape = Shape.wrap(reshape.Apply(old_shape.object)) self._new_shapes.Bind(old_shape.object, new_shape.object)
def _process_unsplit_wing(compound, divide_closed, reloft, tol): # Process a wing that was generated without "Split Surfs" option. faces = compound.faces if len(faces) != 1: return None, None face = faces[0] # Get the surface. master_surf = face.surface # master_surf = NurbsSurface(master_surf.object) uknots, vknots = master_surf.uknots, master_surf.vknots vsplit = master_surf.local_to_global_param('v', 0.5) # Segment off the end caps and the trailing edges. u1, u2 = uknots[1], uknots[-2] v1, v2 = vknots[1], vknots[-2] s1 = master_surf.copy() s1.segment(u1, u2, v1, v2) # Reloft the surface by tessellating a curve at each spanwise knot. This # enforces C1 continuity but assumes linear spanwise wing which may not # support blending wing sections in newer versions of OpenVSP. Also, since # the tessellated curves may not match up to the wing end caps making # sewing unreliable, flat end caps are assumed. if reloft: s1 = _reloft_wing_surface(s1, tol) # Generate new flat end caps using isocurves at the root and tip of # this new surface c0 = s1.v_iso(s1.v1) c1 = s1.v_iso(s1.v2) e0 = Edge.by_curve(c0) e1 = Edge.by_curve(c1) w0 = Wire.by_edge(e0) w1 = Wire.by_edge(e1) f0 = Face.by_wire(w0) f1 = Face.by_wire(w1) # Make faces of surfaces f = Face.by_surface(s1) new_faces = [f, f0, f1] else: # Reparamterize knots in spanwise direction to be chord length instead # of uniform. Use isocurve at quarter-chord to determine knot values. # This only works as long as surfaces are linear. c0 = s1.u_iso(s1.u1) c0.segment(vsplit, c0.u2) qc_u = PointFromParameter(c0, vsplit, 0.25 * c0.length).parameter c = s1.v_iso(qc_u) pnts = [c.eval(u) for u in c.knots] new_uknots = geom_utils.chord_parameters(pnts, 0., 1.) s1.set_uknots(new_uknots) # Segment off end caps u1, u2 = uknots[0], uknots[1] v1, v2 = vknots[1], vsplit s2 = master_surf.copy() s2.segment(u1, u2, v1, v2) u1, u2 = uknots[0], uknots[1] v1, v2 = vsplit, vknots[-2] s3 = master_surf.copy() s3.segment(u1, u2, v1, v2) u1, u2 = uknots[-2], uknots[-1] v1, v2 = vknots[1], vsplit s4 = master_surf.copy() s4.segment(u1, u2, v1, v2) u1, u2 = uknots[-2], uknots[-1] v1, v2 = vsplit, vknots[-2] s5 = master_surf.copy() s5.segment(u1, u2, v1, v2) # Make faces of surfaces new_faces = [] for s in [s1, s2, s3, s4, s5]: f = Face.by_surface(s) new_faces.append(f) # Segment off TE. u1, u2 = uknots[0], uknots[-1] v1, v2 = vknots[0], vknots[1] s6 = master_surf.copy() s6.segment(u1, u2, v1, v2) u1, u2 = uknots[0], uknots[-1] v1, v2 = vknots[-2], vknots[-1] s7 = master_surf.copy() s7.segment(u1, u2, v1, v2) # Split the TE surface at each u-knot. usplits = occ_utils.to_tcolstd_hseq_real(uknots[1:-1]) split = ShapeUpgrade_SplitSurface() split.Init(s6.object) split.SetUSplitValues(usplits) split.Perform() comp_surf1 = split.ResSurfaces() split = ShapeUpgrade_SplitSurface() split.Init(s7.object) split.SetUSplitValues(usplits) split.Perform() comp_surf2 = split.ResSurfaces() # For each patch in the composite surfaces create a face. for i in range(1, comp_surf1.NbUPatches() + 1): for j in range(1, comp_surf1.NbVPatches() + 1): hpatch = comp_surf1.Patch(i, j) f = BRepBuilderAPI_MakeFace(hpatch, 0.).Face() new_faces.append(f) for i in range(1, comp_surf2.NbUPatches() + 1): for j in range(1, comp_surf2.NbVPatches() + 1): hpatch = comp_surf2.Patch(i, j) f = BRepBuilderAPI_MakeFace(hpatch, 0.).Face() new_faces.append(f) # Put all faces into a compound a generate solid. new_compound = Compound.by_shapes(new_faces) return _build_solid(new_compound, divide_closed)
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
def export_step(self, fn, label_solids=True, label_faces=False, names=None): """ Export the OpenVSP model as a STEP file using Extended Data Exchange. Each OpenVSP component will be a named product in the STEP file structure. :param str fn: The filename. :param bool label_solids: Option to label the solid bodies in the STEP entity. The name will be the same as the OpenVSP component. :param bool label_faces: Option to label the faces in each of the solids. Each face of the solid body will be labeled "Face 1", "Face 2", etc. :param names: List of Body names that will be included in export. If *None* then all are exported. :type names: collections.Sequence(str) or None :return: None. """ # Initialize the document doc = XdeDocument() # Get bodies to include if names is None: bodies = self.all_bodies else: bodies = [self.get_body(name) for name in names] # Gather OpenVSP bodies and names and build single compound solids = [] names = [] for body in bodies: solids.append(body.shape) names.append(body.name) cmp = Compound.by_shapes(solids) # Add main shape and top-level assembly main = doc.add_shape(cmp, 'Vehicle') # Each body should be a product so names are transferred to other # CAD systems for name, solid in zip(names, solids): doc.add_subshape(main, solid, name) # Transfer the document and then modify the STEP item name directly # rather than using a label. This only applies when labeling # sub-shapes. doc.transfer_step() if label_solids or label_faces: for name, solid in zip(names, solids): if label_solids: doc.set_shape_name(solid, name) if label_faces: i = 1 for f in solid.faces: name = ' '.join(['Face', str(i)]) doc.set_shape_name(f, name) i += 1 doc.write_step(fn)