docs: def get_boundaries_of_elements_in_dict(models_dict, clearance=0.):
      """ Get boundaries of all handed models
  
      The function checks all model meta data positions to increase boundary starting with a state or scoped variables.
      It is finally iterated over all states, data and logical port models and linkage if sufficient for respective
      graphical editor. At the end a clearance is added to the boundary if needed e.g. to secure size for opengl.
  
      :param models_dict: dict of all handed models
      :return: tuple of left, right, top and bottom value
      """
  
      # Determine initial outer coordinates
      right = 0.
      bottom = 0.
      if 'states' in models_dict and models_dict['states']:
          left = list(models_dict['states'].items())[0][1].get_meta_data_editor()['rel_pos'][0]
          top = list(models_dict['states'].items())[0][1].get_meta_data_editor()['rel_pos'][1]
      elif 'scoped_variables' in models_dict and models_dict['scoped_variables']:
          left = list(models_dict['scoped_variables'].items())[0][1].get_meta_data_editor()['inner_rel_pos'][0]
          top = list(models_dict['scoped_variables'].items())[0][1].get_meta_data_editor()['inner_rel_pos'][1]
      else:
          all_ports = list(models_dict['input_data_ports'].values()) + list(models_dict['output_data_ports'].values()) + \
                      list(models_dict['scoped_variables'].values()) + list(models_dict['outcomes'].values())
          if len(set([port_m.core_element.parent for port_m in all_ports])) == 1:
              logger.info("Only one parent {0} {1}".format(all_ports[0].core_element.parent, all_ports[0].parent.get_meta_data_editor()))
          if all_ports:
              left = all_ports[0].parent.get_meta_data_editor()['rel_pos'][0]
              top = all_ports[0].parent.get_meta_data_editor()['rel_pos'][1]
          else:
              raise ValueError("Get boundary method does not aspects all list elements empty in dictionary. {0}"
                               "".format(models_dict))
  
      def cal_max(max_x, max_y, rel_pos, size):
          max_x = size[0] + rel_pos[0] if size[0] + rel_pos[0] > max_x else max_x
          max_y = rel_pos[1] + size[1] if rel_pos[1] + size[1] > max_y else max_y
          return max_x, max_y
  
      def cal_min(min_x, min_y, rel_pos, size):
          min_x = rel_pos[0] if rel_pos[0] < min_x else min_x
          min_y = rel_pos[1] if rel_pos[1] < min_y else min_y
          return min_x, min_y
  
      # Finally take into account all relevant elements in models_dict
      # -> states, scoped variables (maybe input- and output- data ports) and transitions and data flows are relevant
      parts = ['states', 'transitions', 'data_flows']
      for key in parts:
          elems_dict = models_dict[key]
          rel_positions = []
          for model in elems_dict.values():
              _size = (0., 0.)
              if key == 'states':
                  rel_positions = [model.get_meta_data_editor()['rel_pos']]
                  _size = model.get_meta_data_editor()['size']
                  # print(key, rel_positions, _size)
              elif key in ['scoped_variables', 'input_data_ports', 'output_data_ports']:
                  rel_positions = [model.get_meta_data_editor()['inner_rel_pos']]
                  # TODO check to take the ports size into account
                  # print(key, rel_positions, _size)
              elif key in ['transitions', 'data_flows']:
                  if key is "data_flows":
                      # take into account the meta data positions of opengl if there is some (always in opengl format)
                      rel_positions = mirror_waypoints(deepcopy(model.get_meta_data_editor()))['waypoints']
                  else:
                      rel_positions = model.get_meta_data_editor()['waypoints']
                  # print(key, rel_positions, _size, model.meta)
  
              for rel_position in rel_positions:
                  # check for empty fields and ignore them at this point
                  if not contains_geometric_info(rel_position):
                      continue
                  right, bottom = cal_max(right, bottom, rel_position, _size)
                  left, top = cal_min(left, top, rel_position, _size)
                  # print("new edges:", left, right, top, bottom, key)
  
      # increase of boundary results into bigger estimated size and finally stronger reduction of original element sizes
      left, right, top, bottom = add_boundary_clearance(left, right, top, bottom, {'size': (0., 0.)}, clearance)
      return left, right, top, bottom