def run(self): """ Extracts floors in the following steps: 1. Searchs for the specified object. 2. Splits the surfaces which point upwards at a specified level away. """ mesh_objects = [] for obj in self.config.get_list("selector"): if obj.type != "MESH": warnings.warn( "The object: {} is not a mesh but was selected in the FloorExtractor!" .format(obj.name)) continue mesh_objects.append(MeshObject(obj)) floors = FloorExtractor.extract( mesh_objects=mesh_objects, compare_angle_degrees=radians( self.config.get_float('compare_angle_degrees', 7.5)), compare_height=self.config.get_float('compare_height', 0.15), new_name_for_object=self.config.get_string("name_for_split_obj", "Floor"), should_skip_if_object_is_already_there=self.config.get_bool( "should_skip_if_object_is_already_there", False)) add_properties = self.config.get_raw_dict("add_properties", {}) if add_properties: config = Config({"add_properties": add_properties}) loader_interface = LoaderInterface(config) loader_interface._set_properties(floors)
def run(self): if not os.path.exists(self.json_path): raise Exception("The given path does not exists: {}".format( self.json_path)) if not self.json_path.endswith(".json"): raise Exception( "The given path does not point to a .json file: {}".format( self.json_path)) if not os.path.exists(self.future_model_path): raise Exception( "The 3D future model path does not exist: {}".format( self.future_model_path)) # load data from json file with open(self.json_path, "r") as json_file: data = json.load(json_file) self._create_mesh_objects_from_file(data) all_loaded_furniture = self._load_furniture_objs(data) self._move_and_duplicate_furniture(data, all_loaded_furniture) # add an identifier to the obj for obj in self.created_objects: obj["is_3d_front"] = True LoaderInterface._set_properties(self.created_objects)
def run(self): """ Extracts floors in the following steps: 1. Searchs for the specified object. 2. Splits the surfaces which point upwards at a specified level away. """ entities = self.config.get_list("selector") compare_angle = radians( self.config.get_float('compare_angle_degrees', 7.5)) compare_height = self.config.get_float('compare_height', 0.15) new_name_for_object = self.config.get_string("name_for_split_obj", "Floor") add_properties = self.config.get_raw_dict("add_properties", {}) # set the up_vector up_vec = mathutils.Vector([0, 0, 1]) up_vec_upwards = self.config.get_bool("up_vector_upwards", True) if not up_vec_upwards: up_vec *= -1.0 height_list = [] if self.config.has_param("height_list_path"): height_file_path = Utility.resolve_path( self.config.get_string('height_list_path')) with open(height_file_path) as file: import ast height_list = [ float(val) for val in ast.literal_eval(file.read()) ] bpy.ops.object.select_all(action='DESELECT') newly_created_objects = [] for obj in entities: if obj.type != "MESH": warnings.warn( "The object: {} is not a mesh but was selected in the FloorExtractor!" .format(obj.name)) continue obj.select_set(True) bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode='EDIT') mesh = obj.data bm = bmesh.from_edit_mesh(mesh) bm.faces.ensure_lookup_table() if height_list: bpy.ops.mesh.select_all(action='DESELECT') counter = 0 for height_val in height_list: counter = FloorExtractor.split_at_height_value( bm, height_val, compare_height, up_vec, compare_angle, obj.matrix_world) if counter: bpy.ops.mesh.separate(type='SELECTED') else: try: from sklearn.cluster import MeanShift, estimate_bandwidth except ImportError: raise ImportError( "If no height_list_path is defined, the sklearn lib has to be installed: " "By adding \"scikit-learn\" to the \"setup\"/\"pip\" in the config file." ) # no height list was provided, try to estimate them on its own # first get a list of all height values of the median points, which are inside of the defined # compare angle range list_of_median_poses = [ FloorExtractor.get_median_face_pose(f, obj.matrix_world)[2] for f in bm.faces if FloorExtractor.check_face_angle( f, obj.matrix_world, up_vec, compare_angle) ] list_of_median_poses = np.reshape(list_of_median_poses, (-1, 1)) # The following bandwidth can be automatically detected using bandwidth = estimate_bandwidth(list_of_median_poses, quantile=0.2, n_samples=500) ms = MeanShift(bandwidth=bandwidth, bin_seeding=True) ms.fit(list_of_median_poses) # if the up vector is negative the maximum value is searched if up_vec_upwards: height_value = np.min(ms.cluster_centers_) else: height_value = np.max(ms.cluster_centers_) bpy.ops.mesh.select_all(action='DESELECT') counter = FloorExtractor.split_at_height_value( bm, height_value, compare_height, up_vec, compare_angle, obj.matrix_world) if counter: bpy.ops.mesh.separate(type='SELECTED') selected_objects = bpy.context.selected_objects if selected_objects: if len(selected_objects) == 2: selected_objects = [ o for o in selected_objects if o != bpy.context.view_layer.objects.active ] selected_objects[0].name = new_name_for_object newly_created_objects.append(selected_objects[0]) else: raise Exception( "There is more than one selection after splitting, this should not happen!" ) else: raise Exception("No floor object was constructed!") bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') if add_properties: config = Config({"add_properties": add_properties}) loader_interface = LoaderInterface(config) loader_interface._set_properties(newly_created_objects)
def run(self): """ Extracts floors in the following steps: 1. Searchs for the specified object. 2. Splits the surfaces which point upwards at a specified level away. """ entities = self.config.get_list("selector") compare_angle = radians( self.config.get_float('compare_angle_degrees', 7.5)) compare_height = self.config.get_float('compare_height', 0.15) new_name_for_object = self.config.get_string("name_for_split_obj", "Floor") add_properties = self.config.get_raw_dict("add_properties", {}) should_skip_if_object_is_already_there = self.config.get_bool( "should_skip_if_object_is_already_there", False) # set the up_vector up_vec = mathutils.Vector([0, 0, 1]) up_vec_upwards = self.config.get_bool("up_vector_upwards", True) if not up_vec_upwards: up_vec *= -1.0 height_list = [] if self.config.has_param("height_list_path"): height_file_path = Utility.resolve_path( self.config.get_string('height_list_path')) with open(height_file_path) as file: import ast height_list = [ float(val) for val in ast.literal_eval(file.read()) ] object_names = [ obj.name for obj in bpy.context.scene.objects if obj.type == "MESH" ] def clean_up_name(name: str): """ Clean up the given name from Floor1 to floor :param name: given name :return: str: cleaned up name """ name = ''.join([i for i in name if not i.isdigit()]) # remove digits name = name.lower().replace( ".", "").strip() # remove dots and whitespace return name object_names = [clean_up_name(name) for name in object_names] if should_skip_if_object_is_already_there and new_name_for_object.lower( ) in object_names: # if should_skip is True and if there is an object, which name is the same as the one for the newly # split object, than the execution is skipped return bpy.ops.object.select_all(action='DESELECT') newly_created_objects = [] for obj in entities: if obj.type != "MESH": warnings.warn( "The object: {} is not a mesh but was selected in the FloorExtractor!" .format(obj.name)) continue obj.select_set(True) bpy.context.view_layer.objects.active = obj bpy.ops.object.mode_set(mode='EDIT') mesh = obj.data bm = bmesh.from_edit_mesh(mesh) bm.faces.ensure_lookup_table() if height_list: bpy.ops.mesh.select_all(action='DESELECT') counter = 0 for height_val in height_list: counter = FloorExtractor.split_at_height_value( bm, height_val, compare_height, up_vec, compare_angle, obj.matrix_world) if counter: bpy.ops.mesh.separate(type='SELECTED') else: try: from sklearn.cluster import MeanShift, estimate_bandwidth except ImportError: raise ImportError( "If no height_list_path is defined, the sklearn lib has to be installed: " "By adding \"scikit-learn\" to the \"setup\"/\"pip\" in the config file." ) # no height list was provided, try to estimate them on its own # first get a list of all height values of the median points, which are inside of the defined # compare angle range list_of_median_poses = [ FloorExtractor.get_median_face_pose(f, obj.matrix_world)[2] for f in bm.faces if FloorExtractor.check_face_angle( f, obj.matrix_world, up_vec, compare_angle) ] if not list_of_median_poses: print( "Object with name: {} is skipped no faces were relevant, try with " "flipped up_vec".format(obj.name)) list_of_median_poses = [ FloorExtractor.get_median_face_pose( f, obj.matrix_world)[2] for f in bm.faces if FloorExtractor.check_face_angle( f, obj.matrix_world, -up_vec, compare_angle) ] if not list_of_median_poses: print("Still no success for: {} skip object.".format( obj.name)) bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') continue successful_up_vec = -up_vec else: successful_up_vec = up_vec list_of_median_poses = np.reshape(list_of_median_poses, (-1, 1)) if np.var(list_of_median_poses) < 1e-4: # All faces are already correct height_value = np.mean(list_of_median_poses) else: ms = MeanShift(bandwidth=0.2, bin_seeding=True) ms.fit(list_of_median_poses) # if the up vector is negative the maximum value is searched if up_vec_upwards: height_value = np.min(ms.cluster_centers_) else: height_value = np.max(ms.cluster_centers_) bpy.ops.mesh.select_all(action='DESELECT') counter = FloorExtractor.split_at_height_value( bm, height_value, compare_height, successful_up_vec, compare_angle, obj.matrix_world) if counter: bpy.ops.mesh.separate(type='SELECTED') selected_objects = bpy.context.selected_objects if selected_objects: if len(selected_objects) == 2: selected_objects = [ o for o in selected_objects if o != bpy.context.view_layer.objects.active ] selected_objects[0].name = new_name_for_object newly_created_objects.append(selected_objects[0]) else: raise Exception( "There is more than one selection after splitting, this should not happen!" ) else: raise Exception("No floor object was constructed!") bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.select_all(action='DESELECT') if add_properties: config = Config({"add_properties": add_properties}) loader_interface = LoaderInterface(config) loader_interface._set_properties(newly_created_objects)