parameters: (objects, delete=False, force=None)
return_statement: return add_list, delete_list
docstring: Downgrade the given objects.
  
      This is a counterpart to `upgrade`.
  
      Parameters
      ----------
      objects: Part::Feature or list
          A single object to downgrade or a list
          containing various such objects.
  
      delete: bool, optional
          It defaults to `False`.
          If it is `True`, the old objects are deleted, and only the resulting
          object is kept.
  
      force: str, optional
          It defaults to `None`.
          Its value can be used to force a certain method of downgrading.
          It can be any of: `'explode'`, `'shapify'`, `'subtr'`, `'splitFaces'`,
          `'cut2'`, `'getWire'`, `'splitWires'`, or `'splitCompounds'`.
  
      Returns
      -------
      tuple
          A tuple containing two lists, a list of new objects
          and a list of objects to be deleted.
  
      None
          If there is a problem it will return `None`.
  
      See Also
      --------
      ugrade
docstring_summary: Downgrade the given objects.
docstring_tokens: Downgrade the given objects .
function: def downgrade(objects, delete=False, force=None):
      """Downgrade the given objects.
  
      This is a counterpart to `upgrade`.
  
      Parameters
      ----------
      objects: Part::Feature or list
          A single object to downgrade or a list
          containing various such objects.
  
      delete: bool, optional
          It defaults to `False`.
          If it is `True`, the old objects are deleted, and only the resulting
          object is kept.
  
      force: str, optional
          It defaults to `None`.
          Its value can be used to force a certain method of downgrading.
          It can be any of: `'explode'`, `'shapify'`, `'subtr'`, `'splitFaces'`,
          `'cut2'`, `'getWire'`, `'splitWires'`, or `'splitCompounds'`.
  
      Returns
      -------
      tuple
          A tuple containing two lists, a list of new objects
          and a list of objects to be deleted.
  
      None
          If there is a problem it will return `None`.
  
      See Also
      --------
      ugrade
      """
      _name = "downgrade"
      utils.print_header(_name, "Downgrade objects")
  
      if not isinstance(objects, list):
          objects = [objects]
  
      delete_list = []
      add_list = []
      doc = App.ActiveDocument
  
      # actions definitions
      def explode(obj):
          """Explode a Draft block."""
          pl = obj.Placement
          newobj = []
          for o in obj.Components:
              o.Placement = o.Placement.multiply(pl)
              if App.GuiUp:
                  o.ViewObject.Visibility = True
          if newobj:
              delete_list(obj)
              return newobj
          return None
  
      def cut2(objects):
          """Cut first object from the last one."""
          newobj = cut.cut(objects[0], objects[1])
          if newobj:
              add_list.append(newobj)
              return newobj
          return None
  
      def splitCompounds(objects):
          """Split solids contained in compound objects into new objects."""
          result = False
          for o in objects:
              if o.Shape.Solids:
                  for s in o.Shape.Solids:
                      newobj = doc.addObject("Part::Feature", "Solid")
                      newobj.Shape = s
                      add_list.append(newobj)
                  result = True
                  delete_list.append(o)
          return result
  
      def splitFaces(objects):
          """Split faces contained in objects into new objects."""
          result = False
          params = App.ParamGet("User parameter:BaseApp/Preferences/Mod/Draft")
          preserveFaceColor = params.GetBool("preserveFaceColor")  # True
          preserveFaceNames = params.GetBool("preserveFaceNames")  # True
          for o in objects:
              if App.GuiUp and preserveFaceColor and o.ViewObject:
                  voDColors = o.ViewObject.DiffuseColor
              else:
                  voDColors = None
              oLabel = o.Label if hasattr(o, 'Label') else ""
              if o.Shape.Faces:
                  for ind, f in enumerate(o.Shape.Faces):
                      newobj = doc.addObject("Part::Feature", "Face")
                      newobj.Shape = f
                      if preserveFaceNames:
                          newobj.Label = "{} {}".format(oLabel, newobj.Label)
                      if App.GuiUp and preserveFaceColor and voDColors:
                          # At this point, some single-color objects might have
                          # just a single value in voDColors for all faces,
                          # so we handle that
                          if ind < len(voDColors):
                              tcolor = voDColors[ind]
                          else:
                              tcolor = voDColors[0]
                          # does is not applied visually on its own
                          # just in case
                          newobj.ViewObject.DiffuseColor[0] = tcolor
                          # this gets applied, works by itself too
                          newobj.ViewObject.ShapeColor = tcolor
                      add_list.append(newobj)
                  result = True
                  delete_list.append(o)
          return result
  
      def subtr(objects):
          """Subtract objects from the first one."""
          faces = []
          for o in objects:
              if o.Shape.Faces:
                  faces.extend(o.Shape.Faces)
                  delete_list.append(o)
          u = faces.pop(0)
          for f in faces:
              u = u.cut(f)
          if not u.isNull():
              newobj = doc.addObject("Part::Feature", "Subtraction")
              newobj.Shape = u
              add_list.append(newobj)
              return newobj
          return None
  
      def getWire(obj):
          """Get the wire from a face object."""
          result = False
          for w in obj.Shape.Faces[0].Wires:
              newobj = doc.addObject("Part::Feature", "Wire")
              newobj.Shape = w
              add_list.append(newobj)
              result = True
          delete_list.append(obj)
          return result
  
      def splitWires(objects):
          """Split the wires contained in objects into edges."""
          result = False
          for o in objects:
              if o.Shape.Edges:
                  for e in o.Shape.Edges:
                      newobj = doc.addObject("Part::Feature", "Edge")
                      newobj.Shape = e
                      add_list.append(newobj)
                  delete_list.append(o)
                  result = True
          return result
  
      # analyzing objects
      faces = []
      edges = []
      onlyedges = True
      parts = []
      solids = []
      result = None
  
      for o in objects:
          if hasattr(o, 'Shape'):
              for s in o.Shape.Solids:
                  solids.append(s)
              for f in o.Shape.Faces:
                  faces.append(f)
              for e in o.Shape.Edges:
                  edges.append(e)
              if o.Shape.ShapeType != "Edge":
                  onlyedges = False
              parts.append(o)
      objects = parts
  
      if force:
          if force in ("explode", "shapify", "subtr", "splitFaces",
                       "cut2", "getWire", "splitWires"):
              # TODO: Using eval to evaluate a string is not ideal
              # and potentially a security risk.
              # How do we execute the function without calling eval?
              # Best case, a series of if-then statements.
              shapify = utils.shapify
              result = eval(force)(objects)
          else:
              _msg(translate("draft","Upgrade: Unknown force method:") + " " + force)
              result = None
      else:
          # applying transformation automatically
          # we have a block, we explode it
          if len(objects) == 1 and utils.get_type(objects[0]) == "Block":
              result = explode(objects[0])
              if result:
                  _msg(translate("draft","Found 1 block: exploding it"))
  
          # we have one multi-solids compound object: extract its solids
          elif (len(objects) == 1 and hasattr(objects[0], 'Shape')
                and len(solids) > 1):
              result = splitCompounds(objects)
              # print(result)
              if result:
                  _msg(translate("draft","Found 1 multi-solids compound: exploding it"))
  
          # special case, we have one parametric object: we "de-parametrize" it
          elif (len(objects) == 1 and hasattr(objects[0], 'Shape')
                and hasattr(objects[0], 'Base')):
              result = utils.shapify(objects[0])
              if result:
                  _msg(translate("draft","Found 1 parametric object: breaking its dependencies"))
                  add_list.append(result)
                  # delete_list.append(objects[0])
  
          # we have only 2 objects: cut 2nd from 1st
          elif len(objects) == 2:
              result = cut2(objects)
              if result:
                  _msg(translate("draft","Found 2 objects: subtracting them"))
  
          elif len(faces) > 1:
              # one object with several faces: split it
              if len(objects) == 1:
                  result = splitFaces(objects)
                  if result:
                      _msg(translate("draft","Found several faces: splitting them"))
              # several objects: remove all the faces from the first one
              else:
                  result = subtr(objects)
                  if result:
                      _msg(translate("draft","Found several objects: subtracting them from the first one"))
          # only one face: we extract its wires
          elif len(faces) > 0:
              result = getWire(objects[0])
              if result:
                  _msg(translate("draft","Found 1 face: extracting its wires"))
  
          # no faces: split wire into single edges
          elif not onlyedges:
              result = splitWires(objects)
              if result:
                  _msg(translate("draft","Found only wires: extracting their edges"))
  
          # no result has been obtained
          if not result:
              _msg(translate("draft","No more downgrade possible"))
  
      if delete:
          names = []
          for o in delete_list:
              names.append(o.Name)
          delete_list = []
          for n in names:
              doc.removeObject(n)
  
      gui_utils.select(add_list)
      return add_list, delete_list
function_tokens: def downgrade ( objects , delete = False , force = None ) : _name = "downgrade" utils . print_header ( _name , "Downgrade objects" ) if not isinstance ( objects , list ) : objects = [ objects ] delete_list = [ ] add_list = [ ] doc = App . ActiveDocument # actions definitions def explode ( obj ) : """Explode a Draft block.""" pl = obj . Placement newobj = [ ] for o in obj . Components : o . Placement = o . Placement . multiply ( pl ) if App . GuiUp : o . ViewObject . Visibility = True if newobj : delete_list ( obj ) return newobj return None def cut2 ( objects ) : """Cut first object from the last one.""" newobj = cut . cut ( objects [ 0 ] , objects [ 1 ] ) if newobj : add_list . append ( newobj ) return newobj return None def splitCompounds ( objects ) : """Split solids contained in compound objects into new objects.""" result = False for o in objects : if o . Shape . Solids : for s in o . Shape . Solids : newobj = doc . addObject ( "Part::Feature" , "Solid" ) newobj . Shape = s add_list . append ( newobj ) result = True delete_list . append ( o ) return result def splitFaces ( objects ) : """Split faces contained in objects into new objects.""" result = False params = App . ParamGet ( "User parameter:BaseApp/Preferences/Mod/Draft" ) preserveFaceColor = params . GetBool ( "preserveFaceColor" ) # True preserveFaceNames = params . GetBool ( "preserveFaceNames" ) # True for o in objects : if App . GuiUp and preserveFaceColor and o . ViewObject : voDColors = o . ViewObject . DiffuseColor else : voDColors = None oLabel = o . Label if hasattr ( o , 'Label' ) else "" if o . Shape . Faces : for ind , f in enumerate ( o . Shape . Faces ) : newobj = doc . addObject ( "Part::Feature" , "Face" ) newobj . Shape = f if preserveFaceNames : newobj . Label = "{} {}" . format ( oLabel , newobj . Label ) if App . GuiUp and preserveFaceColor and voDColors : # At this point, some single-color objects might have # just a single value in voDColors for all faces, # so we handle that if ind < len ( voDColors ) : tcolor = voDColors [ ind ] else : tcolor = voDColors [ 0 ] # does is not applied visually on its own # just in case newobj . ViewObject . DiffuseColor [ 0 ] = tcolor # this gets applied, works by itself too newobj . ViewObject . ShapeColor = tcolor add_list . append ( newobj ) result = True delete_list . append ( o ) return result def subtr ( objects ) : """Subtract objects from the first one.""" faces = [ ] for o in objects : if o . Shape . Faces : faces . extend ( o . Shape . Faces ) delete_list . append ( o ) u = faces . pop ( 0 ) for f in faces : u = u . cut ( f ) if not u . isNull ( ) : newobj = doc . addObject ( "Part::Feature" , "Subtraction" ) newobj . Shape = u add_list . append ( newobj ) return newobj return None def getWire ( obj ) : """Get the wire from a face object.""" result = False for w in obj . Shape . Faces [ 0 ] . Wires : newobj = doc . addObject ( "Part::Feature" , "Wire" ) newobj . Shape = w add_list . append ( newobj ) result = True delete_list . append ( obj ) return result def splitWires ( objects ) : """Split the wires contained in objects into edges.""" result = False for o in objects : if o . Shape . Edges : for e in o . Shape . Edges : newobj = doc . addObject ( "Part::Feature" , "Edge" ) newobj . Shape = e add_list . append ( newobj ) delete_list . append ( o ) result = True return result # analyzing objects faces = [ ] edges = [ ] onlyedges = True parts = [ ] solids = [ ] result = None for o in objects : if hasattr ( o , 'Shape' ) : for s in o . Shape . Solids : solids . append ( s ) for f in o . Shape . Faces : faces . append ( f ) for e in o . Shape . Edges : edges . append ( e ) if o . Shape . ShapeType != "Edge" : onlyedges = False parts . append ( o ) objects = parts if force : if force in ( "explode" , "shapify" , "subtr" , "splitFaces" , "cut2" , "getWire" , "splitWires" ) : # TODO: Using eval to evaluate a string is not ideal # and potentially a security risk. # How do we execute the function without calling eval? # Best case, a series of if-then statements. shapify = utils . shapify result = eval ( force ) ( objects ) else : _msg ( translate ( "draft" , "Upgrade: Unknown force method:" ) + " " + force ) result = None else : # applying transformation automatically # we have a block, we explode it if len ( objects ) == 1 and utils . get_type ( objects [ 0 ] ) == "Block" : result = explode ( objects [ 0 ] ) if result : _msg ( translate ( "draft" , "Found 1 block: exploding it" ) ) # we have one multi-solids compound object: extract its solids elif ( len ( objects ) == 1 and hasattr ( objects [ 0 ] , 'Shape' ) and len ( solids ) > 1 ) : result = splitCompounds ( objects ) # print(result) if result : _msg ( translate ( "draft" , "Found 1 multi-solids compound: exploding it" ) ) # special case, we have one parametric object: we "de-parametrize" it elif ( len ( objects ) == 1 and hasattr ( objects [ 0 ] , 'Shape' ) and hasattr ( objects [ 0 ] , 'Base' ) ) : result = utils . shapify ( objects [ 0 ] ) if result : _msg ( translate ( "draft" , "Found 1 parametric object: breaking its dependencies" ) ) add_list . append ( result ) # delete_list.append(objects[0]) # we have only 2 objects: cut 2nd from 1st elif len ( objects ) == 2 : result = cut2 ( objects ) if result : _msg ( translate ( "draft" , "Found 2 objects: subtracting them" ) ) elif len ( faces ) > 1 : # one object with several faces: split it if len ( objects ) == 1 : result = splitFaces ( objects ) if result : _msg ( translate ( "draft" , "Found several faces: splitting them" ) ) # several objects: remove all the faces from the first one else : result = subtr ( objects ) if result : _msg ( translate ( "draft" , "Found several objects: subtracting them from the first one" ) ) # only one face: we extract its wires elif len ( faces ) > 0 : result = getWire ( objects [ 0 ] ) if result : _msg ( translate ( "draft" , "Found 1 face: extracting its wires" ) ) # no faces: split wire into single edges elif not onlyedges : result = splitWires ( objects ) if result : _msg ( translate ( "draft" , "Found only wires: extracting their edges" ) ) # no result has been obtained if not result : _msg ( translate ( "draft" , "No more downgrade possible" ) ) if delete : names = [ ] for o in delete_list : names . append ( o . Name ) delete_list = [ ] for n in names : doc . removeObject ( n ) gui_utils . select ( add_list ) return add_list , delete_list