def select_deselect_edge_lists(root_edge, loop=True): kw = {'edgeLoop' if loop else 'edgeRing': root_edge.index} if root_edge in mampy.complist(): kw.update({'deselect': True}) else: kw.update({'add': True}) cmds.polySelect(root_edge.dagpath, **kw)
def get_face_weighted_sets(): sets = cmds.ls('{}*'.format(_face_weighted_name)) selected, to_weight = mampy.daglist(), ComponentList() if selected: for each in selected: set_name = get_face_weighted_set_name(each.transform.short_name) if set_name in sets: to_weight.extend(mampy.complist(cmds.sets(set_name, q=True))) else: for set_name in sets: try: to_weight.extend(mampy.complist(cmds.sets(set_name, q=True))) except ValueError: continue return to_weight
def vertex_normals_from_selection_center(): bbox = BoundingBox() for component in mampy.complist(): if not component.is_vert(): component = component.to_vert() bbox.expand(component.bbox) set_vertex_normals_on_selected_from_vector(bbox.center)
def set_vertex_normals_on_selected_from_vector(vector): for component in mampy.complist(): if not component.is_vert(): component = component.to_vert() for index in component: normal = (component.points[index] - vector).normalize() component.mesh.setVertexNormal(normal, index, space=api.MSpace.kWorld)
def select_deselect_border_edge(root_edge, tolerance): def get_vector_from_edge(edge, index): p1, p2 = [edge.points[i] for i in edge.vertices[index]] return p2 - p1 root_edge_vector = get_vector_from_edge(root_edge, root_edge.index) edge_border_indices = cmds.polySelect( root_edge.dagpath, edgeBorder=root_edge.index, noSelection=True ) border_edge = root_edge.new().add(edge_border_indices) edges_to_select = root_edge.new() for idx in border_edge: border_edge_vector = get_vector_from_edge(border_edge, idx) if root_edge_vector.isParallel(border_edge_vector, tolerance): edges_to_select.add(idx) connected = edges_to_select.get_connected_components() for e in connected: if root_edge.index in e: if root_edge in mampy.complist(): cmds.select(e.cmdslist(), d=True) else: cmds.select(e.cmdslist(), add=True)
def detach(extract=False): """ Extracts or duplicate selected polygon faces. """ selected = mampy.complist() if not selected: raise InvalidSelection('Detach only works on edge and polygon components.') else: control = selected[0] if control.type not in [MFn.kMeshPolygonComponent, MFn.kMeshEdgeComponent]: raise InvalidSelection('Detach only works on edges and polygons.') new = ComponentList() for comp in selected: node = Node(comp.dagpath) name = '{}_{}'.format(node.transform.short_name, 'ext' if extract else 'dup') cmds.duplicate(str(comp.dagpath), n=name) # Delete unwanted children from duplicate hierarchy dupdag = Node(name) for child in dupdag.iterchildren(): if child.type == MFn.kTransform: cmds.delete(str(child)) if extract: cmds.polyDelFacet(comp.cmdslist()) dupcomp = MeshPolygon.create(dupdag.dagpath).add(comp.indices) cmds.polyDelFacet(dupcomp.toggle().cmdslist()) cmds.hilite(str(dupdag)) new.append(dupcomp.get_complete()) cmds.select(new.cmdslist(), r=True)
def flatten(averaged=True): """ Flattens selection by averaged normal. """ def flatten(component, script_job=False): if script_job: cmds.select(selected.cmdslist()) else: cmds.select(component.cmdslist()) center = list(component.bbox.center)[:3] origin = get_average_vert_normal(component.normals, component.indices) # Perform scale cmds.manipScaleContext('Scale', e=True, mode=6, alignAlong=origin) radians = cmds.manipScaleContext('Scale', q=True, orientAxes=True) t = [math.degrees(r) for r in radians] cmds.scale(0, 1, 1, r=True, oa=t, p=center) def script_job(): """ Get normal from script job selection and pass it to flatten. """ flatten(next(iter(mampy.complist())).to_vert(), True) selected = mampy.complist() if averaged: for comp in selected: flatten(comp.to_vert()) cmds.select(selected.cmdslist(), r=True) else: # Scale selection to given selection. cmds.scriptJob(event=['SelectionChanged', script_job], runOnce=True)
def select_deselect_surrounded(root_comp): selected = mampy.complist() if not selected: cmds.selecet(root_comp.get_complete().cmdslist(), add=True) else: for comp in selected: # Find correct dagpath to work on if not root_comp.dagpath == comp.dagpath: continue if comp.is_complete(): cmds.select(comp.cmdslist(), d=True) else: connected = list(comp.get_connected_components()) connected_unselected = list(comp.toggle().get_connected_components()) if any(root_comp.index in c for c in connected): kw = {'deselect': True} iterable = connected elif any(root_comp.index in c for c in connected_unselected): kw = {'add': True} iterable = connected_unselected for c in iterable: if root_comp.index in c: cmds.select(c.cmdslist(), **kw)
def merge_faces(): """Removes edges inside of face selection.""" selected = mampy.complist() if not selected: raise NothingSelected() control_object = next(iter(selected)) if not control_object.type == MFn.kMeshPolygonComponent or not len(control_object) > 1: raise InvalidSelection("Must have at least two connected faces selected.") new_faces = ComponentList() for face in selected: # We must first collect all necessary elements before we operate on them. # This is to avoid getting uncertain information due to indices changing # when performing the delete function. border_vertices = ComponentList() internal_edges = ComponentList() for connected_face in face.get_connected_components(): border_vertices.append(connected_face.to_vert(border=True)) internal_edges.append(connected_face.to_edge(internal=True)) # We only delete once per object to perserve as much information as # possible. cmds.polyDelEdge(internal_edges.cmdslist()) # Collect the most shared face on the border vertices to get new faces # from the delete operation. for border_vert in border_vertices: counter = collections.Counter() for idx in border_vert.indices: f = border_vert.new().add(idx).to_face() counter.update(f.indices) new_faces.append(face.new().add(counter.most_common(1).pop()[0])) # Select and be happy! cmds.select(new_faces.cmdslist())
def bevel(): """ Bevel is a collection of functions usually performed when certain states are fulfilled, such as: selection, border edge etc. """ selected = mampy.complist() if not selected: raise NothingSelected() component = selected[0] # Eearly return in case of map components if component.type == MFn.kMeshMapComponent: raise InvalidComponentSelection() if component.type == api.MFn.kMeshEdgeComponent: # Check if we can extrude edge borders or bevel internal edges. if all(component.is_border(i) for i in component.indices): # When extruding border edges we don't want the context tool to handle # the manipulation. Just restore old tool right away. with restore_context(): dragger_contexts.Extrude().execute() elif not any(component.is_border(i) for i in component.indices): dragger_contexts.Bevel().set_context() else: raise InvalidSelection('Mixed selections, cant mix edge borders with ' 'non borders when beveling.') else: # Else perform an extrude on polygon or vertex. if cmds.currentCtx() == dragger_contexts.Extrude.NAME: dragger_contexts.Extrude().execute() else: dragger_contexts.Extrude().set_context()
def bridge(): selected = mampy.complist() if not selected: raise NothingSelected() # Quit early if face selections. if selected[0].type == api.MFn.kMeshPolygonComponent: bridge_face() # Only work on border components for component in get_borders_from_complist(selected): component = component.to_edge(internal=True) connected = list(component.get_connected_components()) # If border is closed or edges are next to each others if len(connected) == 1: border_edge = get_border_loop_indices_from_edge_index(component.index) if border_edge == set(component.indices): cmds.polyCloseBorder(component.cmdslist()) else: cmds.polyAppend( str(component.dagpath), s=1, a=(component.index, component.indices[-1]) ) else: cmds.polyBridgeEdge(divisions=0)
def unbevel(): """ Unbevel beveled edges. Select Edges along a bevel you want to unbevel. Make sure the edge is not connected to another edge from another bevel. This will cause the script to get confused. """ selected = mampy.complist() for edge in selected: # cmds.select(edge.cmdslist(), r=True) merge_list = ComponentList() for each in edge.get_connected_components(): outer_edges, inner_verts = get_outer_and_inner_edges_from_edge_loop(each) edge1, edge2 = outer_edges line1 = Line3D(edge1[0].bbox.center, edge1[1].bbox.center) line2 = Line3D(edge2[0].bbox.center, edge2[1].bbox.center) intersection = line1.shortest_line_to_other(line2) inner_verts.translate(t=intersection.sum() * 0.5, ws=True) merge_list.append(inner_verts) # Merge components on object after all operation are done. Mergin # before will change vert ids and make people sad. cmds.polyMergeVertex(merge_list.cmdslist(), distance=0.001)
def bridge_face(): faces = mampy.complist() # Get edge border before deleting face. mamselect.mesh.convert('edge') # Now safe to delete face and perform bridge. cmds.delete(faces.cmdslist()) cmds.polyBridgeEdge(divisions=0)
def inbetween(): """Select components between the last two selections.""" # TODO: refactor and finish. ordered_selection = mampy.complist(os=True) if not len(ordered_selection) % 2 == 0: comp1, comp2 = ordered_selection[-2:] else: for comp in ordered_selection: cmds.polySelect(comp.cmdslist(), q=True, loop=True)
def deselect_all_but(): preselect = mampy.complist(preSelectHilite=True) obj = get_object_under_cursor() if not obj: return cmds.select(obj, r=True) if not preselect: return else: cmds.hilite(obj, toggle=True)
def spin_edge(offset=1): """ Spin all selected edges. Allows us to spin edges within a face selection. """ selected = mampy.complist() for comp in selected: if not comp.is_edge(): comp = comp.to_edge(internal=True) cmds.polySpinEdge(comp.cmdslist(), offset=offset, ch=False) cmds.select(cl=True); cmds.select(selected.cmdslist(), r=True)
def connect(): """ Control flow for connected component tools. """ selected = mampy.complist() if selected: object_type = selected.pop().type if object_type in [MFn.kMeshMapComponent]: raise InvalidComponentSelection() elif object_type == MFn.kMeshVertComponent: mamtools.mel('ConnectComponents') else: mamtools.mel('connectTool')
def merge_verts(move): """Merges verts to first selection.""" ordered_selection = mampy.complist(os=True) if move or len(ordered_selection) == 2: if len(next(iter(ordered_selection))) > 1: pass else: if not move: v1, v2 = ordered_selection pos = v1.bbox.expand(v2.bbox).center else: pos = ordered_selection.pop().bbox.center cmds.xform(ordered_selection.cmdslist(), t=list(pos)[:3], ws=True) cmds.polyMergeVertex(ordered_selection.cmdslist(), distance=0.001, ch=True)
def detach(extract=False): """ Dispatches function call depening on selection type or type of panel under cursor. """ selected = mampy.complist() or mampy.daglist() if not selected: raise NothingSelected() object_type = selected.pop().type if object_type == MFn.kMeshPolygonComponent: mamtools.mesh.detach(extract) elif object_type in [api.MFn.kTransform]: cmds.duplicate(rr=True)
def flood(): """Get contiguous components from current selection.""" selected = mampy.complist() if not selected: raise NothingSelected() flood = ComponentList() for comp in selected: if comp.type == MFn.kMeshMapComponent: iter_ = comp.map_shells else: iter_ = comp.mesh_shells flood.extend(iter_.itervalues()) cmds.select(flood.cmdslist())
def collapse(): selected = mampy.complist() if not selected: return logger.warn("Invalid component selection.") for comp in selected: if comp.type == MFn.kMeshEdgeComponent: for comp in comp.get_connected_components(): for edge in comp.indices: vert = MeshVert.create(comp.dagpath).add(comp.vertices[edge]) vert.translate(t=list(comp.bbox.center)[:3], ws=True) else: vert = comp.to_vert() vert.translate(t=list(vert.bbox.center)[:3], ws=True) cmds.polyMergeVertex(comp.cmdslist(), distance=0.001) cmds.select(cl=True)
def select_deselect_isolated_components(loop=True, tolerance=0.35): """Clear mesh or loop under mouse.""" preselect = mampy.complist(preSelectHilite=True) if not preselect: raise NothingSelected() preselect_component = preselect.pop() if preselect_component.type == api.MFn.kMeshEdgeComponent: if not loop: select_deselect_edge_lists(preselect_component, loop) elif preselect_component.is_border(preselect_component.index): select_deselect_border_edge(preselect_component, tolerance) else: select_deselect_edge_lists(preselect_component, loop) else: select_deselect_surrounded(preselect_component)
def toggle_mesh_under_cursor(): """Toggle mesh object under cursor.""" preselect = mampy.complist(preSelectHilite=True) if not preselect: under_cursor_mesh = get_object_under_cursor() if under_cursor_mesh is None: return node = Node(under_cursor_mesh) if cmds.selectMode(q=True, component=True): cmds.hilite(str(node.transform)) else: cmds.select(str(node), toggle=True) else: node = preselect.pop().mdag if node.transform in mampy.daglist(hl=True): cmds.hilite(str(node.transform), unHilite=True)
def poly_invert(shell=False): """ Invert selection. If shell is active but there are no selections, script assumes we want a full invert. .. note:: If current selection mask is *object* and there are no selections there is no way that I know of to find out the active component type. """ # To find out how we want to operate on the objects we walk through # the possible outcomes leaving the object list at last. modes = [mampy.complist(), mampy.daglist(hl=True), mampy.daglist()] for mode, selected in enumerate(modes): if not selected: continue break if mode == 2: if not selected: cmds.select(mampy.daglist(visible=True, assemblies=True).cmdslist()) else: cmds.select(selected.cmdslist(), toggle=True) if mode == 1: for mask in get_active_flags_in_mask(object=False): try: active_mask = { 'facet': MFn.kMeshPolygonComponent, 'edge': MFn.kMeshEdgeComponent, 'vertex': MFn.kMeshVertComponent, 'polymeshUV': MFn.kMeshMapComponent, }[mask]; break except KeyError: continue for dag in selected: component = SingleIndexComponent.create(dag.dagpath, active_mask) cmds.select(component.get_complete().cmdslist(), toggle=True) if mode == 0: selection_list = ComponentList() for comp in selected: if shell: for mesh in comp.mesh_shells.itervalues(): selection_list.append(mesh) else: selection_list.append(comp.get_complete()) cmds.select(selection_list.cmdslist(), toggle=True)
def set_face_weighted_normals_sets(add=True): faces = mampy.complist() for face in faces: name = face.mdag.transform.short_name set_name = get_face_weighted_set_name(name) try: result = bool(cmds.sets(set_name, q=True)) except ValueError: result = False if result: if add: cmds.sets(face.cmdslist(), addElement=set_name) else: cmds.sets(face.cmdslist(), remove=set_name) else: cmds.sets(name=set_name)
def delete(cv=False): """Custom delete using 'right' delete function depending on selection.""" selected = mampy.complist() or mampy.daglist() if not selected: return logger.warn("Nothing to delete.") for each in selected: if isinstance(each, SingleIndexComponent): # Try to delete supported types if that fails uss default delete in # maya try: { MFn.kMeshEdgeComponent: partial(cmds.polyDelEdge, each.cmdslist(), cv=cv), MFn.kMeshVertComponent: partial(cmds.polyDelVertex, each.cmdslist()), MFn.kMeshPolygonComponent: partial(cmds.polyDelFacet, each.cmdslist()), }[each.type]() except KeyError: cmds.delete(each.cmdslist()) else: cmds.delete(str(each)) cmds.select(cl=True)
def adjacent(): """Grow and remove previous selection to get adjacent selection. .. todo:: make contractable """ selected = mampy.complist() if not selected: raise NothingSelected() toggle_components = ComponentList() for each in selected: try: adjacent_selection = { MFn.kMeshPolygonComponent: each.to_edge().to_face(), MFn.kMeshEdgeComponent: each.to_vert().to_edge(), MFn.kMeshVertComponent: each.to_edge().to_vert(), MFn.kMeshMapComponent: each.to_edge().to_map(), }[each.type] toggle_components.append(adjacent_selection) except KeyError: raise InvalidSelection('Selection must be mesh component.') cmds.select(toggle_components.cmdslist(), toggle=True)
def convert(comptype, **convert_arguments): """ Convert current selection to given comptype. """ ComponentType = collections.namedtuple('ComponentType', ('type', 'function')) convert_mode = { 'vert': ComponentType(api.MFn.kMeshVertComponent, 'to_vert'), 'edge': ComponentType(api.MFn.kMeshEdgeComponent, 'to_edge'), 'face': ComponentType(api.MFn.kMeshPolygonComponent, 'to_face'), 'map': ComponentType(api.MFn.kMeshMapComponent, 'to_map'), }[comptype] selected, converted = mampy.complist(), ComponentList() if not selected: raise NothingSelected() for comp in selected: if comp.type == convert_mode: continue converted.append(getattr(comp, convert_mode.function)(**convert_arguments)) set_selection_mask(comptype) cmds.select(converted.cmdslist())
def merge(): """ Dispatches function call depending on selection type """ selected = mampy.complist() or mampy.daglist() if not selected: raise NothingSelected() control_object = selected.pop() if control_object.type in [MFn.kTransform]: object_type = control_object.shape.type else: object_type = control_object.type try: { MFn.kMeshVertComponent: functools.partial(mamtools.delete.merge_verts, False), MFn.kMeshPolygonComponent: mamtools.delete.merge_faces, MFn.kMeshEdgeComponent: mamtools.delete.collapse, MFn.kMesh: mamtools.mesh.combine_separate, }[object_type]() except KeyError: logger.warn('{} is not a valid type'.format(object_type))
def script_job(): """ Get normal from script job selection and pass it to flatten. """ flatten(next(iter(mampy.complist())).to_vert(), True)